Merge "libbinder.vendor: remove activity manager AIDL" into sc-dev
diff --git a/Android.bp b/Android.bp
index 298c01a..dec6716 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,4 +85,10 @@
cc_library_headers{
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
-}
\ No newline at end of file
+}
+
+filegroup {
+ name: "deviceproductinfoconstants_aidl",
+ srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"],
+ path: "aidl",
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index a8c0500..922367f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,8 +8,7 @@
include/input/
include/powermanager/
libs/binder/fuzzer/
- libs/binder/ndk/
- libs/binder/tests/fuzzers/
+ libs/binder/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
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/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 3184843..cedff0b 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -62,7 +62,7 @@
using std::string;
-#define MAX_SYS_FILES 11
+#define MAX_SYS_FILES 12
const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
@@ -177,6 +177,7 @@
{ OPT, "events/power/suspend_resume/enable" },
{ OPT, "events/cpuhp/cpuhp_enter/enable" },
{ OPT, "events/cpuhp/cpuhp_exit/enable" },
+ { OPT, "events/cpuhp/cpuhp_pause/enable" },
} },
{ "membus", "Memory Bus Utilization", 0, {
{ REQ, "events/memory_bus/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 006e532..37fc9a9 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -57,6 +57,8 @@
chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_enter/enable
chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit/enable
chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_pause/enable
+ chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_pause/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable
chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index e15ac3f..c2c8a72 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -64,8 +64,8 @@
if (shutdown_ || threads_.empty()) {
return;
}
- while (!tasks_.empty()) tasks_.pop();
futures_map_.clear();
+ while (!tasks_.empty()) tasks_.pop();
shutdown_ = true;
condition_variable_.notify_all();
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 990aa53..7ab2a8d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1208,10 +1208,6 @@
static void DumpPacketStats() {
DumpFile("NETWORK DEV INFO", "/proc/net/dev");
- DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
- DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
- DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
- DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
}
static void DumpIpAddrAndRules() {
@@ -1657,8 +1653,6 @@
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
- /* Dump Bluetooth HCI logs */
- ds.AddDir("/data/misc/bluetooth/logs", true);
/* Dump Nfc NCI logs */
ds.AddDir("/data/misc/nfc/logs", true);
@@ -1744,6 +1738,9 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
+ /* Dump Bluetooth HCI logs after getting bluetooth_manager dumpsys */
+ ds.AddDir("/data/misc/bluetooth/logs", true);
+
if (ds.dump_pool_) {
WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
} else {
@@ -2060,7 +2057,7 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
+ const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
@@ -2417,7 +2414,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()
@@ -2912,6 +2911,10 @@
// own activity pushes out interesting data from the trace ring buffer.
// The trace file is added to the zip by MaybeAddSystemTraceToZip().
MaybeSnapshotSystemTrace();
+
+ // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later
+ // from WMTRACE_DATA_DIR.
+ MaybeSnapshotWinTrace();
}
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
@@ -3023,6 +3026,17 @@
// file in the later stages.
}
+void Dumpstate::MaybeSnapshotWinTrace() {
+ // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
+ for (const auto& service : {"window", "input_method"}) {
+ RunCommand(
+ // Empty name because it's not intended to be classified as a bugreport section.
+ // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
+ "", {"cmd", service, "tracing", "save-for-bugreport"},
+ CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+ }
+}
+
void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
if (calling_uid == AID_SHELL || !CalledByApi()) {
return;
@@ -3067,6 +3081,9 @@
android::os::UnlinkAndLogOnError(tmp_path_);
android::os::UnlinkAndLogOnError(screenshot_path_);
android::os::UnlinkAndLogOnError(path_);
+ if (dump_traces_path != nullptr) {
+ android::os::UnlinkAndLogOnError(dump_traces_path);
+ }
}
void Dumpstate::EnableParallelRunIfNeeded() {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index f83968b..83e6787 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -549,6 +549,7 @@
void MaybeTakeEarlyScreenshot();
void MaybeSnapshotSystemTrace();
+ void MaybeSnapshotWinTrace();
void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c62d302..0712c0a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -411,7 +411,6 @@
}
TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
- SectionExists("HIGH connectivity", /* bytes= */ 3000);
SectionExists("connectivity", /* bytes= */ 5000);
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index b2518ad..2c573e4 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1762,6 +1762,27 @@
EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
}
+TEST_F(DumpPoolTest, Shutdown_withoutCrash) {
+ bool run_1 = false;
+ auto dump_func_1 = [&]() {
+ run_1 = true;
+ };
+ auto dump_func = []() {
+ sleep(1);
+ };
+
+ dump_pool_->start(/* thread_counts = */1);
+ dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+ dump_pool_->enqueueTask(/* task_name = */"2", dump_func);
+ dump_pool_->enqueueTask(/* task_name = */"3", dump_func);
+ dump_pool_->enqueueTask(/* task_name = */"4", dump_func);
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ EXPECT_TRUE(run_1);
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
class TaskQueueTest : public DumpstateBaseTest {
public:
void SetUp() {
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 1ebdc47..ec3bc61 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -49,13 +49,20 @@
"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",
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/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/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/installd/Android.bp b/cmds/installd/Android.bp
index 5c2211f..a546236 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -189,8 +189,8 @@
"liblog",
"libutils",
],
- static_libs: [
- "libapexd",
+ required: [
+ "apexd"
],
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 4dfd1d0..0cf50a3 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -2112,8 +2112,9 @@
{
struct stat s;
if (stat(b_path.c_str(), &s) != 0) {
- // Silently ignore for now. The service calling this isn't smart enough to understand
- // lack of artifacts at the moment.
+ // Ignore for now. The service calling this isn't smart enough to
+ // understand lack of artifacts at the moment.
+ LOG(VERBOSE) << "A/B artifact " << b_path << " does not exist!";
return false;
}
if (!S_ISREG(s.st_mode)) {
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 443821c..ed31ad9 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -175,8 +175,10 @@
private:
bool ReadSystemProperties() {
+ // TODO This file does not have a stable format. It should be read by
+ // code shared by init and otapreopt. See b/181182967#comment80
static constexpr const char* kPropertyFiles[] = {
- "/default.prop", "/system/build.prop"
+ "/system/build.prop"
};
for (size_t i = 0; i < arraysize(kPropertyFiles); ++i) {
@@ -193,28 +195,38 @@
// export NAME VALUE
// For simplicity, don't respect string quotation. The values we are interested in can be
// encoded without them.
+ // init.environ.rc and etc/classpath have the same format for
+ // environment variable exports and can be matched by the same regex.
+ // TODO Just like with the system-properties above we really should have
+ // common code between init and otapreopt to deal with reading these
+ // things. See b/181182967
+ static constexpr const char* kEnvironmentVariableSources[] = {
+ "/init.environ.rc", "/etc/classpath"
+ };
+
std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
- bool parse_result = ParseFile("/init.environ.rc", [&](const std::string& line) {
- std::smatch export_match;
- if (!std::regex_match(line, export_match, export_regex)) {
+ for (const char* env_vars_file : kEnvironmentVariableSources) {
+ bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) {
+ std::smatch export_match;
+ if (!std::regex_match(line, export_match, export_regex)) {
+ return true;
+ }
+
+ if (export_match.size() != 3) {
+ return true;
+ }
+
+ std::string name = export_match[1].str();
+ std::string value = export_match[2].str();
+
+ system_properties_.SetProperty(name, value);
+
return true;
+ });
+ if (!parse_result) {
+ return false;
}
-
- if (export_match.size() != 3) {
- return true;
- }
-
- std::string name = export_match[1].str();
- std::string value = export_match[2].str();
-
- system_properties_.SetProperty(name, value);
-
- return true;
- });
- if (!parse_result) {
- return false;
}
-
if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
return false;
}
@@ -337,9 +349,6 @@
}
}
- // Clear cached artifacts.
- ClearDirectory(isa_path);
-
// Check whether we have a boot image.
// TODO: check that the files are correct wrt/ jars.
std::string preopted_boot_art_path =
@@ -383,37 +392,6 @@
return false;
}
- static void ClearDirectory(const std::string& dir) {
- DIR* c_dir = opendir(dir.c_str());
- if (c_dir == nullptr) {
- PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents";
- return;
- }
-
- for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
- const char* name = de->d_name;
- if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
- continue;
- }
- // We only want to delete regular files and symbolic links.
- std::string file = StringPrintf("%s/%s", dir.c_str(), name);
- if (de->d_type != DT_REG && de->d_type != DT_LNK) {
- LOG(WARNING) << "Unexpected file "
- << file
- << " of type "
- << std::hex
- << de->d_type
- << " encountered.";
- } else {
- // Try to unlink the file.
- if (unlink(file.c_str()) != 0) {
- PLOG(ERROR) << "Unable to unlink " << file;
- }
- }
- }
- CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
- }
-
static const char* ParseNull(const char* arg) {
return (strcmp(arg, "!") == 0) ? nullptr : arg;
}
@@ -473,24 +451,29 @@
// Run dexopt with the parameters of parameters_.
// TODO(calin): embed the profile name in the parameters.
int Dexopt() {
- std::string dummy;
- return dexopt(parameters_.apk_path,
- parameters_.uid,
- parameters_.pkgName,
- parameters_.instruction_set,
- parameters_.dexopt_needed,
- parameters_.oat_dir,
- parameters_.dexopt_flags,
- parameters_.compiler_filter,
- parameters_.volume_uuid,
- parameters_.shared_libraries,
- parameters_.se_info,
- parameters_.downgrade,
- parameters_.target_sdk_version,
- parameters_.profile_name,
- parameters_.dex_metadata_path,
- parameters_.compilation_reason,
- &dummy);
+ std::string error;
+ int res = dexopt(parameters_.apk_path,
+ parameters_.uid,
+ parameters_.pkgName,
+ parameters_.instruction_set,
+ parameters_.dexopt_needed,
+ parameters_.oat_dir,
+ parameters_.dexopt_flags,
+ parameters_.compiler_filter,
+ parameters_.volume_uuid,
+ parameters_.shared_libraries,
+ parameters_.se_info,
+ parameters_.downgrade,
+ parameters_.target_sdk_version,
+ parameters_.profile_name,
+ parameters_.dex_metadata_path,
+ parameters_.compilation_reason,
+ &error);
+ if (res != 0) {
+ LOG(ERROR) << "During preopt of " << parameters_.apk_path << " got result " << res
+ << " error: " << error;
+ }
+ return res;
}
int RunPreopt() {
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 72c03bf..c04b558 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -20,16 +20,18 @@
#include <sys/stat.h>
#include <sys/wait.h>
+#include <array>
+#include <fstream>
#include <sstream>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <libdm/dm.h>
#include <selinux/android.h>
-#include <apexd.h>
-
#include "installd_constants.h"
#include "otapreopt_utils.h"
@@ -59,28 +61,14 @@
}
}
-static std::vector<apex::ApexFile> ActivateApexPackages() {
- // The logic here is (partially) copied and adapted from
- // system/apex/apexd/apexd.cpp.
- //
- // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir).
- std::vector<const char*> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir,
- apex::kApexPackageVendorDir};
- for (const auto& dir : apex_dirs) {
- // Cast call to void to suppress warn_unused_result.
- static_cast<void>(apex::ScanPackagesDirAndActivate(dir));
- }
- return apex::GetActivePackages();
-}
+static void ActivateApexPackages() {
+ std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--otachroot-bootstrap"};
+ std::string apexd_error_msg;
-static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
- for (const apex::ApexFile& apex_file : active_packages) {
- const std::string& package_path = apex_file.GetPath();
- base::Result<void> status = apex::DeactivatePackage(package_path);
- if (!status.ok()) {
- LOG(ERROR) << "Failed to deactivate " << package_path << ": "
- << status.error();
- }
+ bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
+ if (!exec_result) {
+ PLOG(ERROR) << "Running otapreopt failed: " << apexd_error_msg;
+ exit(220);
}
}
@@ -181,6 +169,13 @@
// want it for product APKs. Same notes as vendor above.
TryExtraMount("product", arg[2], "/postinstall/product");
+ constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
+ // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
+ if (mount("tmpfs", kPostInstallLinkerconfig, "tmpfs", 0, nullptr) != 0) {
+ PLOG(ERROR) << "Failed to mount a tmpfs for " << kPostInstallLinkerconfig;
+ exit(215);
+ }
+
// Setup APEX mount point and its security context.
static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
// The following logic is similar to the one in system/core/rootdir/init.rc:
@@ -238,18 +233,48 @@
// Try to mount APEX packages in "/apex" in the chroot dir. We need at least
// the ART APEX, as it is required by otapreopt to run dex2oat.
- std::vector<apex::ApexFile> active_packages = ActivateApexPackages();
+ ActivateApexPackages();
// Check that an ART APEX has been activated; clean up and exit
// early otherwise.
- if (std::none_of(active_packages.begin(),
- active_packages.end(),
- [](const apex::ApexFile& package){
- return package.GetManifest().name() == "com.android.art";
- })) {
- LOG(FATAL_WITHOUT_ABORT) << "No activated com.android.art APEX package.";
- DeactivateApexPackages(active_packages);
- exit(217);
+ static constexpr const std::string_view kRequiredApexs[] = {
+ "com.android.art",
+ "com.android.runtime",
+ };
+ std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false };
+ DIR* apex_dir = opendir("/apex");
+ if (apex_dir == nullptr) {
+ PLOG(ERROR) << "unable to open /apex";
+ exit(220);
+ }
+ for (dirent* entry = readdir(apex_dir); entry != nullptr; entry = readdir(apex_dir)) {
+ for (int i = 0; i < found_apexs.size(); i++) {
+ if (kRequiredApexs[i] == std::string_view(entry->d_name)) {
+ found_apexs[i] = true;
+ break;
+ }
+ }
+ }
+ closedir(apex_dir);
+ auto it = std::find(found_apexs.cbegin(), found_apexs.cend(), false);
+ if (it != found_apexs.cend()) {
+ LOG(ERROR) << "No activated " << kRequiredApexs[std::distance(found_apexs.cbegin(), it)]
+ << " package!";
+ exit(221);
+ }
+
+ // Setup /linkerconfig. Doing it after the chroot means it doesn't need its own category
+ if (selinux_android_restorecon("/linkerconfig", 0) < 0) {
+ PLOG(ERROR) << "Failed to restorecon /linkerconfig";
+ exit(219);
+ }
+ std::vector<std::string> linkerconfig_cmd{"/apex/com.android.runtime/bin/linkerconfig",
+ "--target", "/linkerconfig"};
+ std::string linkerconfig_error_msg;
+ bool linkerconfig_exec_result = Exec(linkerconfig_cmd, &linkerconfig_error_msg);
+ if (!linkerconfig_exec_result) {
+ LOG(ERROR) << "Running linkerconfig failed: " << linkerconfig_error_msg;
+ exit(218);
}
// Now go on and run otapreopt.
@@ -272,9 +297,6 @@
LOG(ERROR) << "Running otapreopt failed: " << error_msg;
}
- // Tear down the work down by the apexd logic. (i.e. deactivate packages).
- DeactivateApexPackages(active_packages);
-
if (!exec_result) {
exit(213);
}
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/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 3c53c02..bbbe6f7 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -495,7 +495,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,
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 83b6aa0..9d88ca6 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -1,7 +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,
-}
\ No newline at end of file
+}
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/imagedecoder.h b/include/android/imagedecoder.h
index cac67d4..ee1eee2 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -367,6 +367,10 @@
* 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 width Width of the output (prior to cropping).
diff --git a/include/android/input.h b/include/android/input.h
index 7973487..bb98beb 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -166,6 +166,9 @@
/** Capture event */
AINPUT_EVENT_TYPE_CAPTURE = 4,
+
+ /** Drag event */
+ AINPUT_EVENT_TYPE_DRAG = 5,
};
/**
@@ -675,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,
/**
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/sensor.h b/include/android/sensor.h
index 6447844..92b79c7 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -448,12 +448,14 @@
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;
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index b836581..eb9c528 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -325,6 +325,38 @@
const ARect& destination, int32_t transform)
__INTRODUCED_IN(29);
+/**
+ * \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.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setSourceRect(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, const ARect& source)
+ __INTRODUCED_IN(31);
+
+/**
+ * \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.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, const ARect& destination)
+ __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 transfrom use the
+ * NATIVE_WINDOW_TRANSFORM_* enum.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setTransform(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, int32_t transform)
+ __INTRODUCED_IN(31);
/**
* Parameter for ASurfaceTransaction_setBufferTransparency().
@@ -467,3 +499,5 @@
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
+
+/** @} */
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h
index b227b32..4757254 100644
--- a/include/android/surface_texture.h
+++ b/include/android/surface_texture.h
@@ -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/input/Flags.h b/include/input/Flags.h
index 072dd18..b12a9ed 100644
--- a/include/input/Flags.h
+++ b/include/input/Flags.h
@@ -84,7 +84,7 @@
template <typename F>
constexpr std::optional<std::string_view> flag_name(F flag) {
using U = std::underlying_type_t<F>;
- auto idx = __builtin_ctzl(static_cast<U>(flag));
+ auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
return details::flag_names<F>[idx];
}
@@ -280,4 +280,4 @@
} // namespace flag_operators
} // namespace android
-#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file
+#endif // __UI_INPUT_FLAGS_H
diff --git a/include/input/Input.h b/include/input/Input.h
index aa42db8..f9fe6b9 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -792,6 +792,30 @@
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.
@@ -855,6 +879,7 @@
virtual MotionEvent* createMotionEvent() = 0;
virtual FocusEvent* createFocusEvent() = 0;
virtual CaptureEvent* createCaptureEvent() = 0;
+ virtual DragEvent* createDragEvent() = 0;
};
/*
@@ -870,12 +895,14 @@
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;
};
/*
@@ -890,6 +917,7 @@
virtual MotionEvent* createMotionEvent() override;
virtual FocusEvent* createFocusEvent() override;
virtual CaptureEvent* createCaptureEvent() override;
+ virtual DragEvent* createDragEvent() override;
void recycle(InputEvent* event);
@@ -900,6 +928,7 @@
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/InputTransport.h b/include/input/InputTransport.h
index 11b714f..3e5674e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -33,6 +33,8 @@
#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>
@@ -44,7 +46,6 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <android-base/unique_fd.h>
namespace android {
class Parcel;
@@ -69,6 +70,7 @@
FINISHED,
FOCUS,
CAPTURE,
+ DRAG,
};
struct Header {
@@ -76,10 +78,15 @@
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 {
int32_t eventId;
@@ -154,8 +161,8 @@
} motion;
struct Finished {
- uint32_t empty1;
- 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); }
@@ -163,39 +170,36 @@
struct Focus {
int32_t eventId;
- // 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;
- uint32_t pointerCaptureEnabled; // actually a bool, but we maintain 8-byte alignment
+ 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;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
size_t size() const;
void getSanitizedCopy(InputMessage* msg) const;
-
- static const char* typeToString(Type type) {
- switch (type) {
- case Type::KEY:
- return "KEY";
- case Type::MOTION:
- return "MOTION";
- case Type::FINISHED:
- return "FINISHED";
- case Type::FOCUS:
- return "FOCUS";
- case Type::CAPTURE:
- return "CAPTURE";
- }
- }
};
/*
@@ -362,20 +366,33 @@
*/
status_t publishCaptureEvent(uint32_t seq, int32_t eventId, bool pointerCaptureEnabled);
- /* Receives the finished signal from the consumer in reply to the original dispatch signal.
- * If a signal was received, returns the message sequence number,
- * whether the consumer handled the message, and the time the event was first read by the
- * consumer.
- *
- * The returned sequence number is never 0 unless the operation failed.
+ /* Publishes a drag 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(
- const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback);
+ 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;
+ };
+
+ /* Receives the finished signal from the consumer in reply to the original dispatch signal.
+ * If a signal was received, returns a Finished object.
+ *
+ * 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<Finished> receiveFinishedSignal();
private:
std::shared_ptr<InputChannel> mChannel;
@@ -609,6 +626,7 @@
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/libs/attestation/Android.bp b/libs/attestation/Android.bp
index b85aecd..ea3c341 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT 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: [
@@ -28,4 +37,4 @@
"liblog",
"libcrypto",
],
-}
\ No newline at end of file
+}
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
index 6ce5ea1..8dac0ce 100644
--- a/libs/attestation/tests/Android.bp
+++ b/libs/attestation/tests/Android.bp
@@ -12,6 +12,15 @@
// 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"],
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index ca15eff..144dbd3 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -122,11 +122,16 @@
"ParcelFileDescriptor.cpp",
"PersistableBundle.cpp",
"ProcessState.cpp",
+ "RpcAddress.cpp",
+ "RpcConnection.cpp",
+ "RpcServer.cpp",
+ "RpcState.cpp",
"Static.cpp",
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
"Utils.cpp",
+ ":packagemanager_aidl",
":libbinder_aidl",
],
@@ -214,17 +219,20 @@
"-misc-redundant-expression",
"-misc-unused-using-decls",
"performance*",
+ "-performance-no-int-to-ptr",
"portability*",
],
+
+ pgo: {
+ sampling: true,
+ profile_file: "libbinder/libbinder.profdata",
+ },
}
// AIDL interface between libbinder and framework.jar
filegroup {
name: "libbinder_aidl",
srcs: [
- "aidl/android/content/pm/IPackageChangeObserver.aidl",
- "aidl/android/content/pm/IPackageManagerNative.aidl",
- "aidl/android/content/pm/PackageChangeEvent.aidl",
"aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
@@ -233,6 +241,16 @@
path: "aidl",
}
+filegroup {
+ name: "packagemanager_aidl",
+ srcs: [
+ "aidl/android/content/pm/IPackageChangeObserver.aidl",
+ "aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/content/pm/PackageChangeEvent.aidl",
+ ],
+ path: "aidl",
+}
+
aidl_interface {
name: "libbinder_aidl_test_stub",
unstable: true,
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index ddda024..825a821 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -21,6 +21,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
+#include <binder/RpcConnection.h>
#include <binder/Stability.h>
#include <cutils/compiler.h>
#include <utils/Log.h>
@@ -133,25 +134,56 @@
}
sTrackingMap[trackedUid]++;
}
- return new BpBinder(handle, trackedUid);
+ return new BpBinder(BinderHandle{handle}, trackedUid);
}
-BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
- : mStability(0)
- , mHandle(handle)
- , mAlive(1)
- , mObitsSent(0)
- , mObituaries(nullptr)
- , mTrackedUid(trackedUid)
-{
- ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);
+BpBinder* BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
+ LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
+ // These are not currently tracked, since there is no UID or other
+ // identifier to track them with. However, if similar functionality is
+ // needed, connection objects keep track of all BpBinder objects on a
+ // per-connection basis.
+
+ return new BpBinder(SocketHandle{connection, address});
+}
+
+BpBinder::BpBinder(Handle&& handle)
+ : mStability(0),
+ mHandle(handle),
+ mAlive(true),
+ mObitsSent(false),
+ mObituaries(nullptr),
+ mTrackedUid(-1) {
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
- IPCThreadState::self()->incWeakHandle(handle, this);
}
-int32_t BpBinder::handle() const {
- return mHandle;
+BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) {
+ mTrackedUid = trackedUid;
+
+ ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle());
+
+ IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
+}
+
+BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) {
+ LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object");
+}
+
+bool BpBinder::isRpcBinder() const {
+ return std::holds_alternative<SocketHandle>(mHandle);
+}
+
+const RpcAddress& BpBinder::rpcAddress() const {
+ return std::get<SocketHandle>(mHandle).address;
+}
+
+const sp<RpcConnection>& BpBinder::rpcConnection() const {
+ return std::get<SocketHandle>(mHandle).connection;
+}
+
+int32_t BpBinder::binderHandle() const {
+ return std::get<BinderHandle>(mHandle).handle;
}
bool BpBinder::isDescriptorCached() const {
@@ -190,9 +222,10 @@
status_t BpBinder::pingBinder()
{
- Parcel send;
+ Parcel data;
+ data.markForBinder(this);
Parcel reply;
- return transact(PING_TRANSACTION, send, &reply);
+ return transact(PING_TRANSACTION, data, &reply);
}
status_t BpBinder::dump(int fd, const Vector<String16>& args)
@@ -236,8 +269,13 @@
}
}
- status_t status = IPCThreadState::self()->transact(
- mHandle, code, data, reply, flags);
+ status_t status;
+ if (CC_UNLIKELY(isRpcBinder())) {
+ status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags);
+ } else {
+ status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
+ }
+
if (status == DEAD_OBJECT) mAlive = 0;
return status;
@@ -250,6 +288,8 @@
status_t BpBinder::linkToDeath(
const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
{
+ if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+
Obituary ob;
ob.recipient = recipient;
ob.cookie = cookie;
@@ -267,10 +307,10 @@
if (!mObituaries) {
return NO_MEMORY;
}
- ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);
+ ALOGV("Requesting death notification: %p handle %d\n", this, binderHandle());
getWeakRefs()->incWeak(this);
IPCThreadState* self = IPCThreadState::self();
- self->requestDeathNotification(mHandle, this);
+ self->requestDeathNotification(binderHandle(), this);
self->flushCommands();
}
ssize_t res = mObituaries->add(ob);
@@ -286,6 +326,8 @@
const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
wp<DeathRecipient>* outRecipient)
{
+ if (isRpcBinder()) return UNKNOWN_TRANSACTION;
+
AutoMutex _l(mLock);
if (mObitsSent) {
@@ -303,9 +345,9 @@
}
mObituaries->removeAt(i);
if (mObituaries->size() == 0) {
- ALOGV("Clearing death notification: %p handle %d\n", this, mHandle);
+ ALOGV("Clearing death notification: %p handle %d\n", this, binderHandle());
IPCThreadState* self = IPCThreadState::self();
- self->clearDeathNotification(mHandle, this);
+ self->clearDeathNotification(binderHandle(), this);
self->flushCommands();
delete mObituaries;
mObituaries = nullptr;
@@ -319,8 +361,10 @@
void BpBinder::sendObituary()
{
- ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
- this, mHandle, mObitsSent ? "true" : "false");
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder.");
+
+ ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(),
+ mObitsSent ? "true" : "false");
mAlive = 0;
if (mObitsSent) return;
@@ -328,9 +372,9 @@
mLock.lock();
Vector<Obituary>* obits = mObituaries;
if(obits != nullptr) {
- ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);
+ ALOGV("Clearing sent death notification: %p handle %d\n", this, binderHandle());
IPCThreadState* self = IPCThreadState::self();
- self->clearDeathNotification(mHandle, this);
+ self->clearDeathNotification(binderHandle(), this);
self->flushCommands();
mObituaries = nullptr;
}
@@ -388,7 +432,9 @@
BpBinder::~BpBinder()
{
- ALOGV("Destroying BpBinder %p handle %d\n", this, mHandle);
+ ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle());
+
+ if (CC_UNLIKELY(isRpcBinder())) return;
IPCThreadState* ipc = IPCThreadState::self();
@@ -396,7 +442,8 @@
AutoMutex _l(sTrackingLock);
uint32_t trackedValue = sTrackingMap[mTrackedUid];
if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) {
- ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, mHandle);
+ ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this,
+ binderHandle());
} else {
if (CC_UNLIKELY(
(trackedValue & LIMIT_REACHED_MASK) &&
@@ -413,26 +460,31 @@
}
if (ipc) {
- ipc->expungeHandle(mHandle, this);
- ipc->decWeakHandle(mHandle);
+ ipc->expungeHandle(binderHandle(), this);
+ ipc->decWeakHandle(binderHandle());
}
}
void BpBinder::onFirstRef()
{
- ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
+ ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle());
+ if (CC_UNLIKELY(isRpcBinder())) return;
IPCThreadState* ipc = IPCThreadState::self();
- if (ipc) ipc->incStrongHandle(mHandle, this);
+ if (ipc) ipc->incStrongHandle(binderHandle(), this);
}
void BpBinder::onLastStrongRef(const void* /*id*/)
{
- ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
+ ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
+ if (CC_UNLIKELY(isRpcBinder())) {
+ (void)rpcConnection()->sendDecStrong(rpcAddress());
+ return;
+ }
IF_ALOGV() {
printRefs();
}
IPCThreadState* ipc = IPCThreadState::self();
- if (ipc) ipc->decStrongHandle(mHandle);
+ if (ipc) ipc->decStrongHandle(binderHandle());
mLock.lock();
Vector<Obituary>* obits = mObituaries;
@@ -442,7 +494,7 @@
mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
}
- if (ipc) ipc->clearDeathNotification(mHandle, this);
+ if (ipc) ipc->clearDeathNotification(binderHandle(), this);
mObituaries = nullptr;
}
mLock.unlock();
@@ -457,9 +509,12 @@
bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/)
{
- ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle);
+ // RPC binder doesn't currently support inc from weak binders
+ if (CC_UNLIKELY(isRpcBinder())) return false;
+
+ ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle());
IPCThreadState* ipc = IPCThreadState::self();
- return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false;
+ return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false;
}
uint32_t BpBinder::getBinderProxyCount(uint32_t uid)
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 88c85bf..349658e 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -15,7 +15,6 @@
*/
#include "BufferedTextOutput.h"
-#include <binder/Debug.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
@@ -26,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include "Debug.h"
#include "Static.h"
// ---------------------------------------------------------------------------
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index 3a62059..8676955 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include <binder/Debug.h>
+#include "Debug.h"
+
#include <binder/ProcessState.h>
#include <utils/misc.h>
@@ -25,6 +26,22 @@
namespace android {
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ const char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+
// ---------------------------------------------------------------------
static const char indentStr[] =
diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/Debug.h
similarity index 89%
rename from libs/binder/include/binder/Debug.h
rename to libs/binder/Debug.h
index ac71e00..7ca087e 100644
--- a/libs/binder/include/binder/Debug.h
+++ b/libs/binder/Debug.h
@@ -17,13 +17,13 @@
#pragma once
#include <stdint.h>
-#include <sys/cdefs.h>
#include <sys/types.h>
+#include <string>
namespace android {
// ---------------------------------------------------------------------------
-__BEGIN_DECLS
+std::string hexString(const void* data, size_t size);
const char* stringForIndent(int32_t indentLevel);
@@ -37,10 +37,7 @@
size_t alignment=0, bool cArrayStyle=false,
debugPrintFunc func = nullptr, void* cookie = nullptr);
-
-ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
-
-__END_DECLS
+extern "C" ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
// ---------------------------------------------------------------------------
} // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 79a11d2..406bd54 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -689,6 +689,8 @@
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
+ LOG_ALWAYS_FATAL_IF(data.isForRpc(), "Parcel constructed for RPC, but being used with binder.");
+
status_t err;
flags |= TF_ACCEPT_FDS;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 3773760..7fedba2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -41,13 +41,15 @@
#include <binder/TextOutput.h>
#include <cutils/ashmem.h>
+#include <cutils/compiler.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
-#include <utils/misc.h>
-#include <utils/String8.h>
#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/misc.h>
#include <private/binder/binder_module.h>
+#include "RpcState.h"
#include "Static.h"
#include "Utils.h"
@@ -191,6 +193,22 @@
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
+ if (isForRpc()) {
+ if (binder) {
+ status_t status = writeInt32(1); // non-null
+ if (status != OK) return status;
+ RpcAddress address = RpcAddress::zero();
+ status = mConnection->state()->onBinderLeaving(mConnection, binder, &address);
+ if (status != OK) return status;
+ status = address.writeToParcel(this);
+ if (status != OK) return status;
+ } else {
+ status_t status = writeInt32(0); // null
+ if (status != OK) return status;
+ }
+ return finishFlattenBinder(binder);
+ }
+
flat_binder_object obj;
obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
@@ -205,8 +223,13 @@
BpBinder *proxy = binder->remoteBinder();
if (proxy == nullptr) {
ALOGE("null proxy");
+ } else {
+ if (proxy->isRpcBinder()) {
+ ALOGE("Sending a socket binder over RPC is prohibited");
+ return INVALID_OPERATION;
+ }
}
- const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0;
+ const int32_t handle = proxy ? proxy->getPrivateAccessorForId().binderHandle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
@@ -245,6 +268,26 @@
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
+ if (isForRpc()) {
+ LOG_ALWAYS_FATAL_IF(mConnection == nullptr,
+ "RpcConnection required to read from remote parcel");
+
+ int32_t isNull;
+ status_t status = readInt32(&isNull);
+ if (status != OK) return status;
+
+ sp<IBinder> binder;
+
+ if (isNull & 1) {
+ auto addr = RpcAddress::zero();
+ status_t status = addr.readFromParcel(*this);
+ if (status != OK) return status;
+ binder = mConnection->state()->onBinderEntering(mConnection, addr);
+ }
+
+ return finishUnflattenBinder(binder, out);
+ }
+
const flat_binder_object* flat = readObject(false);
if (flat) {
@@ -511,6 +554,21 @@
mDeallocZero = true;
}
+void Parcel::markForBinder(const sp<IBinder>& binder) {
+ if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
+ markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
+ }
+}
+
+void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+ LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
+ mConnection = connection;
+}
+
+bool Parcel::isForRpc() const {
+ return mConnection != nullptr;
+}
+
void Parcel::updateWorkSourceRequestHeaderPosition() const {
// Only update the request headers once. We only want to point
// to the first headers read/written.
@@ -533,12 +591,14 @@
}
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
- const IPCThreadState* threadState = IPCThreadState::self();
- writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
- updateWorkSourceRequestHeaderPosition();
- writeInt32(threadState->shouldPropagateWorkSource() ?
- threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
- writeInt32(kHeader);
+ if (CC_LIKELY(!isForRpc())) {
+ const IPCThreadState* threadState = IPCThreadState::self();
+ writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+ updateWorkSourceRequestHeaderPosition();
+ writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid()
+ : IPCThreadState::kUnsetWorkSource);
+ writeInt32(kHeader);
+ }
// currently the interface identification token is just its name as a string
return writeString16(str, len);
@@ -585,31 +645,34 @@
size_t len,
IPCThreadState* threadState) const
{
- // StrictModePolicy.
- int32_t strictPolicy = readInt32();
- if (threadState == nullptr) {
- threadState = IPCThreadState::self();
+ if (CC_LIKELY(!isForRpc())) {
+ // StrictModePolicy.
+ int32_t strictPolicy = readInt32();
+ if (threadState == nullptr) {
+ threadState = IPCThreadState::self();
+ }
+ if ((threadState->getLastTransactionBinderFlags() & IBinder::FLAG_ONEWAY) != 0) {
+ // For one-way calls, the callee is running entirely
+ // disconnected from the caller, so disable StrictMode entirely.
+ // Not only does disk/network usage not impact the caller, but
+ // there's no way to communicate back violations anyway.
+ threadState->setStrictModePolicy(0);
+ } else {
+ threadState->setStrictModePolicy(strictPolicy);
+ }
+ // WorkSource.
+ updateWorkSourceRequestHeaderPosition();
+ int32_t workSource = readInt32();
+ threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+ // vendor header
+ int32_t header = readInt32();
+ if (header != kHeader) {
+ ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader,
+ header);
+ return false;
+ }
}
- if ((threadState->getLastTransactionBinderFlags() &
- IBinder::FLAG_ONEWAY) != 0) {
- // For one-way calls, the callee is running entirely
- // disconnected from the caller, so disable StrictMode entirely.
- // Not only does disk/network usage not impact the caller, but
- // there's no way to commuicate back any violations anyway.
- threadState->setStrictModePolicy(0);
- } else {
- threadState->setStrictModePolicy(strictPolicy);
- }
- // WorkSource.
- updateWorkSourceRequestHeaderPosition();
- int32_t workSource = readInt32();
- threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
- // vendor header
- int32_t header = readInt32();
- if (header != kHeader) {
- ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header);
- return false;
- }
+
// Interface descriptor.
size_t parcel_interface_len;
const char16_t* parcel_interface = readString16Inplace(&parcel_interface_len);
@@ -1070,6 +1133,11 @@
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
{
+ if (isForRpc()) {
+ ALOGE("Cannot write file descriptor to remote binder.");
+ return BAD_TYPE;
+ }
+
flat_binder_object obj;
obj.hdr.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
@@ -2413,6 +2481,7 @@
mDataPos = 0;
ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
+ mConnection = nullptr;
mObjects = nullptr;
mObjectsSize = 0;
mObjectsCapacity = 0;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index edadcd5..abb792e 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -205,10 +205,12 @@
//
// Returns -1 in case of failure, otherwise the strong reference count.
ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) {
+ if (binder->isRpcBinder()) return -1;
+
binder_node_info_for_ref info;
memset(&info, 0, sizeof(binder_node_info_for_ref));
- info.handle = binder->getPrivateAccessorForHandle().handle();
+ info.handle = binder->getPrivateAccessorForId().binderHandle();
status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp
new file mode 100644
index 0000000..5c32320
--- /dev/null
+++ b/libs/binder/RpcAddress.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <binder/RpcAddress.h>
+
+#include <binder/Parcel.h>
+
+#include "Debug.h"
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+namespace android {
+
+RpcAddress RpcAddress::zero() {
+ return RpcAddress();
+}
+
+bool RpcAddress::isZero() const {
+ RpcWireAddress ZERO{0};
+ return memcmp(mRawAddr.get(), &ZERO, sizeof(RpcWireAddress)) == 0;
+}
+
+static void ReadRandomBytes(uint8_t* buf, size_t len) {
+ int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (fd == -1) {
+ ALOGE("%s: cannot read /dev/urandom", __func__);
+ return;
+ }
+
+ size_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, buf, len))) > 0) {
+ len -= n;
+ buf += n;
+ }
+ if (len > 0) {
+ ALOGW("%s: there are %d bytes skipped", __func__, (int)len);
+ }
+ close(fd);
+}
+
+RpcAddress RpcAddress::unique() {
+ RpcAddress ret;
+ ReadRandomBytes((uint8_t*)ret.mRawAddr.get(), sizeof(RpcWireAddress));
+ LOG_RPC_DETAIL("Creating new address: %s", ret.toString().c_str());
+ return ret;
+}
+
+RpcAddress RpcAddress::fromRawEmbedded(const RpcWireAddress* raw) {
+ RpcAddress addr;
+ memcpy(addr.mRawAddr.get(), raw, sizeof(RpcWireAddress));
+ return addr;
+}
+
+const RpcWireAddress& RpcAddress::viewRawEmbedded() const {
+ return *mRawAddr.get();
+}
+
+bool RpcAddress::operator<(const RpcAddress& rhs) const {
+ return std::memcmp(mRawAddr.get(), rhs.mRawAddr.get(), sizeof(RpcWireAddress)) < 0;
+}
+
+std::string RpcAddress::toString() const {
+ return hexString(mRawAddr.get(), sizeof(RpcWireAddress));
+}
+
+status_t RpcAddress::writeToParcel(Parcel* parcel) const {
+ return parcel->write(mRawAddr.get(), sizeof(RpcWireAddress));
+}
+
+status_t RpcAddress::readFromParcel(const Parcel& parcel) {
+ return parcel.read(mRawAddr.get(), sizeof(RpcWireAddress));
+}
+
+RpcAddress::~RpcAddress() {}
+RpcAddress::RpcAddress() : mRawAddr(std::make_shared<RpcWireAddress>()) {}
+
+} // namespace android
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
new file mode 100644
index 0000000..83a1618
--- /dev/null
+++ b/libs/binder/RpcConnection.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 "RpcConnection"
+
+#include <binder/RpcConnection.h>
+
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#if defined(__GLIBC__)
+extern "C" pid_t gettid();
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcConnection::RpcConnection() {
+ LOG_RPC_DETAIL("RpcConnection created %p", this);
+
+ mState = std::make_unique<RpcState>();
+}
+RpcConnection::~RpcConnection() {
+ LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
+}
+
+sp<RpcConnection> RpcConnection::make() {
+ return new RpcConnection;
+}
+
+bool RpcConnection::setupUnixDomainServer(const char* path) {
+ LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now");
+
+ unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket at %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
+ memcpy(addr.sun_path, path, pathLen);
+
+ if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
+ ALOGE("Could not bind socket at %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ ALOGE("Could not listen socket at %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ mServer = std::move(serverFd);
+ return true;
+}
+
+bool RpcConnection::addUnixDomainClient(const char* path) {
+ LOG_RPC_DETAIL("Connecting on path: %s", path);
+
+ unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket at %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
+ memcpy(addr.sun_path, path, pathLen);
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
+ ALOGE("Could not connect socket at %s: %s", path, strerror(errno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get());
+
+ addClient(std::move(serverFd));
+ return true;
+}
+
+sp<IBinder> RpcConnection::getRootObject() {
+ ExclusiveSocket socket(this, SocketUse::CLIENT);
+ return state()->getRootObject(socket.fd(), this);
+}
+
+status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ ExclusiveSocket socket(this,
+ (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
+ : SocketUse::CLIENT);
+ return state()->transact(socket.fd(), address, code, data, this, reply, flags);
+}
+
+status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
+ ExclusiveSocket socket(this, SocketUse::CLIENT_REFCOUNT);
+ return state()->sendDecStrong(socket.fd(), address);
+}
+
+void RpcConnection::join() {
+ // establish a connection
+ {
+ struct sockaddr_un clientSa;
+ socklen_t clientSaLen = sizeof(clientSa);
+
+ unique_fd clientFd(TEMP_FAILURE_RETRY(
+ accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC)));
+ if (clientFd < 0) {
+ // If this log becomes confusing, should save more state from setupUnixDomainServer
+ // in order to output here.
+ ALOGE("Could not accept4 socket: %s", strerror(errno));
+ return;
+ }
+
+ LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+ addServer(std::move(clientFd));
+ }
+
+ // We may not use the connection we just established (two threads might
+ // establish connections for each other), but for now, just use one
+ // server/socket connection.
+ ExclusiveSocket socket(this, SocketUse::SERVER);
+
+ while (true) {
+ status_t error = state()->getAndExecuteCommand(socket.fd(), this);
+
+ if (error != OK) {
+ ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
+ return;
+ }
+ }
+}
+
+void RpcConnection::setForServer(const wp<RpcServer>& server) {
+ mForServer = server;
+}
+
+wp<RpcServer> RpcConnection::server() {
+ return mForServer;
+}
+
+void RpcConnection::addClient(base::unique_fd&& fd) {
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<ConnectionSocket> connection = new ConnectionSocket();
+ connection->fd = std::move(fd);
+ mClients.push_back(connection);
+}
+
+void RpcConnection::addServer(base::unique_fd&& fd) {
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<ConnectionSocket> connection = new ConnectionSocket();
+ connection->fd = std::move(fd);
+ mServers.push_back(connection);
+}
+
+RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
+ : mConnection(connection) {
+ pid_t tid = gettid();
+ std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
+
+ mConnection->mWaitingThreads++;
+ while (true) {
+ sp<ConnectionSocket> exclusive;
+ sp<ConnectionSocket> available;
+
+ // CHECK FOR DEDICATED CLIENT SOCKET
+ //
+ // A server/looper should always use a dedicated connection.
+ if (use != SocketUse::SERVER) {
+ findSocket(tid, &exclusive, &available, mConnection->mClients,
+ mConnection->mClientsOffset);
+
+ // WARNING: this assumes a server cannot request its client to send
+ // a transaction, as mServers is excluded below.
+ //
+ // Imagine we have more than one thread in play, and a single thread
+ // sends a synchronous, then an asynchronous command. Imagine the
+ // asynchronous command is sent on the first client socket. Then, if
+ // we naively send a synchronous command to that same socket, the
+ // thread on the far side might be busy processing the asynchronous
+ // command. So, we move to considering the second available thread
+ // for subsequent calls.
+ if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+ mConnection->mClientsOffset =
+ (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
+ }
+ }
+
+ // USE SERVING SOCKET (to start serving or for nested transaction)
+ //
+ // asynchronous calls cannot be nested
+ if (use != SocketUse::CLIENT_ASYNC) {
+ // servers should start serving on an available thread only
+ // otherwise, this should only be a nested call
+ bool useAvailable = use == SocketUse::SERVER;
+
+ findSocket(tid, &exclusive, (useAvailable ? &available : nullptr),
+ mConnection->mServers, 0 /* index hint */);
+ }
+
+ // if our thread is already using a connection, prioritize using that
+ if (exclusive != nullptr) {
+ mSocket = exclusive;
+ mReentrant = true;
+ break;
+ } else if (available != nullptr) {
+ mSocket = available;
+ mSocket->exclusiveTid = tid;
+ break;
+ }
+
+ LOG_ALWAYS_FATAL_IF(use == SocketUse::SERVER, "Must create connection to join one.");
+
+ // in regular binder, this would usually be a deadlock :)
+ LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0,
+ "Not a client of any connection. You must create a connection to an "
+ "RPC server to make any non-nested (e.g. oneway or on another thread) "
+ "calls.");
+
+ LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...",
+ mConnection->mClients.size(), mConnection->mServers.size());
+ mConnection->mSocketCv.wait(_l);
+ }
+ mConnection->mWaitingThreads--;
+}
+
+void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
+ sp<ConnectionSocket>* available,
+ std::vector<sp<ConnectionSocket>>& sockets,
+ size_t socketsIndexHint) {
+ LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
+ "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
+
+ if (*exclusive != nullptr) return; // consistent with break below
+
+ for (size_t i = 0; i < sockets.size(); i++) {
+ sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
+
+ // take first available connection (intuition = caching)
+ if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
+ *available = socket;
+ continue;
+ }
+
+ // though, prefer to take connection which is already inuse by this thread
+ // (nested transactions)
+ if (exclusive && socket->exclusiveTid == tid) {
+ *exclusive = socket;
+ break; // consistent with return above
+ }
+ }
+}
+
+RpcConnection::ExclusiveSocket::~ExclusiveSocket() {
+ // reentrant use of a connection means something less deep in the call stack
+ // is using this fd, and it retains the right to it. So, we don't give up
+ // exclusive ownership, and no thread is freed.
+ if (!mReentrant) {
+ std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
+ mSocket->exclusiveTid = std::nullopt;
+ if (mConnection->mWaitingThreads > 0) {
+ _l.unlock();
+ mConnection->mSocketCv.notify_one();
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
new file mode 100644
index 0000000..df07916
--- /dev/null
+++ b/libs/binder/RpcServer.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "RpcServer"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <vector>
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <log/log.h>
+#include "RpcState.h"
+
+#include "RpcWireFormat.h"
+
+namespace android {
+
+using base::unique_fd;
+
+RpcServer::RpcServer() {}
+RpcServer::~RpcServer() {}
+
+sp<RpcServer> RpcServer::make() {
+ return new RpcServer;
+}
+
+void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
+ mAgreedExperimental = true;
+}
+
+sp<RpcConnection> RpcServer::addClientConnection() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+
+ auto connection = RpcConnection::make();
+ connection->setForServer(this);
+ mConnections.push_back(connection);
+ return connection;
+}
+
+void RpcServer::setRootObject(const sp<IBinder>& binder) {
+ LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object");
+ mRootObject = binder;
+}
+
+sp<IBinder> RpcServer::getRootObject() {
+ return mRootObject;
+}
+
+} // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
new file mode 100644
index 0000000..64e842e
--- /dev/null
+++ b/libs/binder/RpcState.cpp
@@ -0,0 +1,663 @@
+/*
+ * 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 "RpcState"
+
+#include "RpcState.h"
+
+#include <binder/BpBinder.h>
+#include <binder/RpcServer.h>
+
+#include "Debug.h"
+#include "RpcWireFormat.h"
+
+#include <inttypes.h>
+
+namespace android {
+
+RpcState::RpcState() {}
+RpcState::~RpcState() {}
+
+status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder,
+ RpcAddress* outAddress) {
+ bool isRemote = binder->remoteBinder();
+ bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
+
+ if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) {
+ // We need to be able to send instructions over the socket for how to
+ // connect to a different server, and we also need to let the host
+ // process know that this is happening.
+ ALOGE("Canot send binder from unrelated binder RPC connection.");
+ return INVALID_OPERATION;
+ }
+
+ if (isRemote && !isRpc) {
+ // Without additional work, this would have the effect of using this
+ // process to proxy calls from the socket over to the other process, and
+ // it would make those calls look like they come from us (not over the
+ // sockets). In order to make this work transparently like binder, we
+ // would instead need to send instructions over the socket for how to
+ // connect to the host process, and we also need to let the host process
+ // know this was happening.
+ ALOGE("Cannot send binder proxy %p over sockets", binder.get());
+ return INVALID_OPERATION;
+ }
+
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+
+ // TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map
+ // in RpcState
+ for (auto& [addr, node] : mNodeForAddress) {
+ if (binder == node.binder) {
+ if (isRpc) {
+ const RpcAddress& actualAddr =
+ binder->remoteBinder()->getPrivateAccessorForId().rpcAddress();
+ // TODO(b/182939933): this is only checking integrity of data structure
+ // a different data structure doesn't need this
+ LOG_ALWAYS_FATAL_IF(addr < actualAddr, "Address mismatch");
+ LOG_ALWAYS_FATAL_IF(actualAddr < addr, "Address mismatch");
+ }
+ node.timesSent++;
+ node.sentRef = binder; // might already be set
+ *outAddress = addr;
+ return OK;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(isRpc, "RPC binder must have known address at this point");
+
+ auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::unique(),
+ BinderNode{
+ .binder = binder,
+ .timesSent = 1,
+ .sentRef = binder,
+ }});
+ // TODO(b/182939933): better organization could avoid needing this log
+ LOG_ALWAYS_FATAL_IF(!inserted);
+
+ *outAddress = it->first;
+ return OK;
+}
+
+sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
+ const RpcAddress& address) {
+ std::unique_lock<std::mutex> _l(mNodeMutex);
+
+ if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
+ sp<IBinder> binder = it->second.binder.promote();
+
+ // implicitly have strong RPC refcount, since we received this binder
+ it->second.timesRecd++;
+
+ _l.unlock();
+
+ // We have timesRecd RPC refcounts, but we only need to hold on to one
+ // when we keep the object. All additional dec strongs are sent
+ // immediately, we wait to send the last one in BpBinder::onLastDecStrong.
+ (void)connection->sendDecStrong(address);
+
+ return binder;
+ }
+
+ auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
+ LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");
+
+ // Currently, all binders are assumed to be part of the same connection (no
+ // device global binders in the RPC world).
+ sp<IBinder> binder = BpBinder::create(connection, it->first);
+ it->second.binder = binder;
+ it->second.timesRecd = 1;
+ return binder;
+}
+
+size_t RpcState::countBinders() {
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+ return mNodeForAddress.size();
+}
+
+void RpcState::dump() {
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+ ALOGE("DUMP OF RpcState %p", this);
+ ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size());
+ for (const auto& [address, node] : mNodeForAddress) {
+ sp<IBinder> binder = node.binder.promote();
+
+ const char* desc;
+ if (binder) {
+ if (binder->remoteBinder()) {
+ if (binder->remoteBinder()->isRpcBinder()) {
+ desc = "(rpc binder proxy)";
+ } else {
+ desc = "(binder proxy)";
+ }
+ } else {
+ desc = "(local binder)";
+ }
+ } else {
+ desc = "(null)";
+ }
+
+ ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a:%s type:%s",
+ node.binder.unsafe_get(), node.timesSent, node.timesRecd, address.toString().c_str(),
+ desc);
+ }
+ ALOGE("END DUMP OF RpcState");
+}
+
+void RpcState::terminate() {
+ if (SHOULD_LOG_RPC_DETAIL) {
+ ALOGE("RpcState::terminate()");
+ dump();
+ }
+
+ // if the destructor of a binder object makes another RPC call, then calling
+ // decStrong could deadlock. So, we must hold onto these binders until
+ // mNodeMutex is no longer taken.
+ std::vector<sp<IBinder>> tempHoldBinder;
+
+ {
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+ mTerminated = true;
+ for (auto& [address, node] : mNodeForAddress) {
+ sp<IBinder> binder = node.binder.promote();
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get());
+
+ if (node.sentRef != nullptr) {
+ tempHoldBinder.push_back(node.sentRef);
+ }
+ }
+
+ mNodeForAddress.clear();
+ }
+}
+
+bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
+ LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
+
+ if (size > std::numeric_limits<ssize_t>::max()) {
+ ALOGE("Cannot send %s at size %zu (too big)", what, size);
+ terminate();
+ return false;
+ }
+
+ ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, 0));
+
+ if (sent < 0 || sent != static_cast<ssize_t>(size)) {
+ ALOGE("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent, size,
+ fd.get(), strerror(errno));
+
+ terminate();
+ return false;
+ }
+
+ return true;
+}
+
+bool RpcState::rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size) {
+ if (size > std::numeric_limits<ssize_t>::max()) {
+ ALOGE("Cannot rec %s at size %zu (too big)", what, size);
+ terminate();
+ return false;
+ }
+
+ ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL));
+
+ if (recd < 0 || recd != static_cast<ssize_t>(size)) {
+ terminate();
+
+ if (recd == 0 && errno == 0) {
+ LOG_RPC_DETAIL("No more data when trying to read %s on fd %d", what, fd.get());
+ return false;
+ }
+
+ ALOGE("Failed to read %s (received %zd of %zu bytes) on fd %d, error: %s", what, recd, size,
+ fd.get(), strerror(errno));
+ return false;
+ } else {
+ LOG_RPC_DETAIL("Received %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
+ }
+
+ return true;
+}
+
+sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection) {
+ Parcel data;
+ data.markForRpc(connection);
+ Parcel reply;
+
+ status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
+ connection, &reply, 0);
+ if (status != OK) {
+ ALOGE("Error getting root object: %s", statusToString(status).c_str());
+ return nullptr;
+ }
+
+ return reply.readStrongBinder();
+}
+
+status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
+ const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
+ uint32_t flags) {
+ uint64_t asyncNumber = 0;
+
+ if (!address.isZero()) {
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+ if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
+ auto it = mNodeForAddress.find(address);
+ LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending transact on unknown address %s",
+ address.toString().c_str());
+
+ if (flags & IBinder::FLAG_ONEWAY) {
+ asyncNumber = it->second.asyncNumber++;
+ }
+ }
+
+ if (!data.isForRpc()) {
+ ALOGE("Refusing to send RPC with parcel not crafted for RPC");
+ return BAD_TYPE;
+ }
+
+ if (data.objectsCount() != 0) {
+ ALOGE("Parcel at %p has attached objects but is being used in an RPC call", &data);
+ return BAD_TYPE;
+ }
+
+ RpcWireTransaction transaction{
+ .address = address.viewRawEmbedded(),
+ .code = code,
+ .flags = flags,
+ .asyncNumber = asyncNumber,
+ };
+
+ std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+ memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
+ memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());
+
+ if (transactionData.size() > std::numeric_limits<uint32_t>::max()) {
+ ALOGE("Transaction size too big %zu", transactionData.size());
+ return BAD_VALUE;
+ }
+
+ RpcWireHeader command{
+ .command = RPC_COMMAND_TRANSACT,
+ .bodySize = static_cast<uint32_t>(transactionData.size()),
+ };
+
+ if (!rpcSend(fd, "transact header", &command, sizeof(command))) {
+ return DEAD_OBJECT;
+ }
+ if (!rpcSend(fd, "command body", transactionData.data(), transactionData.size())) {
+ return DEAD_OBJECT;
+ }
+
+ if (flags & IBinder::FLAG_ONEWAY) {
+ return OK; // do not wait for result
+ }
+
+ LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
+
+ return waitForReply(fd, connection, reply);
+}
+
+static void cleanup_data(Parcel* p, const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount) {
+ (void)p;
+ delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
+ (void)dataSize;
+ LOG_ALWAYS_FATAL_IF(objects != nullptr);
+ LOG_ALWAYS_FATAL_IF(objectsCount, 0);
+}
+
+status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+ Parcel* reply) {
+ RpcWireHeader command;
+ while (true) {
+ if (!rpcRec(fd, "command header", &command, sizeof(command))) {
+ return DEAD_OBJECT;
+ }
+
+ if (command.command == RPC_COMMAND_REPLY) break;
+
+ status_t status = processServerCommand(fd, connection, command);
+ if (status != OK) return status;
+ }
+
+ uint8_t* data = new uint8_t[command.bodySize];
+
+ if (!rpcRec(fd, "reply body", data, command.bodySize)) {
+ return DEAD_OBJECT;
+ }
+
+ if (command.bodySize < sizeof(RpcWireReply)) {
+ ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!",
+ sizeof(RpcWireReply), command.bodySize);
+ terminate();
+ return BAD_VALUE;
+ }
+ RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
+ if (rpcReply->status != OK) return rpcReply->status;
+
+ reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
+ nullptr, 0, cleanup_data);
+
+ reply->markForRpc(connection);
+
+ return OK;
+}
+
+status_t RpcState::sendDecStrong(const base::unique_fd& fd, const RpcAddress& addr) {
+ {
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+ if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
+ auto it = mNodeForAddress.find(addr);
+ LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending dec strong on unknown address %s",
+ addr.toString().c_str());
+ LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %s",
+ addr.toString().c_str());
+
+ it->second.timesRecd--;
+ if (it->second.timesRecd == 0 && it->second.timesSent == 0) {
+ mNodeForAddress.erase(it);
+ }
+ }
+
+ RpcWireHeader cmd = {
+ .command = RPC_COMMAND_DEC_STRONG,
+ .bodySize = sizeof(RpcWireAddress),
+ };
+ if (!rpcSend(fd, "dec ref header", &cmd, sizeof(cmd))) return DEAD_OBJECT;
+ if (!rpcSend(fd, "dec ref body", &addr.viewRawEmbedded(), sizeof(RpcWireAddress)))
+ return DEAD_OBJECT;
+ return OK;
+}
+
+status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection) {
+ LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
+
+ RpcWireHeader command;
+ if (!rpcRec(fd, "command header", &command, sizeof(command))) {
+ return DEAD_OBJECT;
+ }
+
+ return processServerCommand(fd, connection, command);
+}
+
+status_t RpcState::processServerCommand(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection,
+ const RpcWireHeader& command) {
+ switch (command.command) {
+ case RPC_COMMAND_TRANSACT:
+ return processTransact(fd, connection, command);
+ case RPC_COMMAND_DEC_STRONG:
+ return processDecStrong(fd, command);
+ }
+
+ // We should always know the version of the opposing side, and since the
+ // RPC-binder-level wire protocol is not self synchronizing, we have no way
+ // to understand where the current command ends and the next one begins. We
+ // also can't consider it a fatal error because this would allow any client
+ // to kill us, so ending the connection for misbehaving client.
+ ALOGE("Unknown RPC command %d - terminating connection", command.command);
+ terminate();
+ return DEAD_OBJECT;
+}
+status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+ const RpcWireHeader& command) {
+ LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
+
+ std::vector<uint8_t> transactionData(command.bodySize);
+ if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
+ return DEAD_OBJECT;
+ }
+
+ return processTransactInternal(fd, connection, std::move(transactionData));
+}
+
+status_t RpcState::processTransactInternal(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection,
+ std::vector<uint8_t>&& transactionData) {
+ if (transactionData.size() < sizeof(RpcWireTransaction)) {
+ ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
+ sizeof(RpcWireTransaction), transactionData.size());
+ terminate();
+ return BAD_VALUE;
+ }
+ RpcWireTransaction* transaction = reinterpret_cast<RpcWireTransaction*>(transactionData.data());
+
+ // TODO(b/182939933): heap allocation just for lookup in mNodeForAddress,
+ // maybe add an RpcAddress 'view' if the type remains 'heavy'
+ auto addr = RpcAddress::fromRawEmbedded(&transaction->address);
+
+ status_t replyStatus = OK;
+ sp<IBinder> target;
+ if (!addr.isZero()) {
+ std::lock_guard<std::mutex> _l(mNodeMutex);
+
+ auto it = mNodeForAddress.find(addr);
+ if (it == mNodeForAddress.end()) {
+ ALOGE("Unknown binder address %s.", addr.toString().c_str());
+ dump();
+ replyStatus = BAD_VALUE;
+ } else {
+ target = it->second.binder.promote();
+ if (target == nullptr) {
+ // This can happen if the binder is remote in this process, and
+ // another thread has called the last decStrong on this binder.
+ // However, for local binders, it indicates a misbehaving client
+ // (any binder which is being transacted on should be holding a
+ // strong ref count), so in either case, terminating the
+ // connection.
+ ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
+ addr.toString().c_str());
+ terminate();
+ replyStatus = BAD_VALUE;
+ } else if (target->localBinder() == nullptr) {
+ ALOGE("Transactions can only go to local binders, not address %s. Terminating!",
+ addr.toString().c_str());
+ terminate();
+ replyStatus = BAD_VALUE;
+ } else if (transaction->flags & IBinder::FLAG_ONEWAY) {
+ if (transaction->asyncNumber != it->second.asyncNumber) {
+ // we need to process some other asynchronous transaction
+ // first
+ // TODO(b/183140903): limit enqueues/detect overfill for bad client
+ // TODO(b/183140903): detect when an object is deleted when it still has
+ // pending async transactions
+ it->second.asyncTodo.push(BinderNode::AsyncTodo{
+ .data = std::move(transactionData),
+ .asyncNumber = transaction->asyncNumber,
+ });
+ LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s", transaction->asyncNumber,
+ addr.toString().c_str());
+ return OK;
+ }
+ }
+ }
+ }
+
+ Parcel data;
+ data.setData(transaction->data, transactionData.size() - offsetof(RpcWireTransaction, data));
+ data.markForRpc(connection);
+
+ Parcel reply;
+ reply.markForRpc(connection);
+
+ if (replyStatus == OK) {
+ if (target) {
+ replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
+ } else {
+ LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
+ // special case for 'zero' address (special server commands)
+ switch (transaction->code) {
+ case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+ sp<IBinder> root;
+ sp<RpcServer> server = connection->server().promote();
+ if (server) {
+ root = server->getRootObject();
+ } else {
+ ALOGE("Root object requested, but no server attached.");
+ }
+
+ replyStatus = reply.writeStrongBinder(root);
+ break;
+ }
+ default: {
+ replyStatus = UNKNOWN_TRANSACTION;
+ }
+ }
+ }
+ }
+
+ if (transaction->flags & IBinder::FLAG_ONEWAY) {
+ if (replyStatus != OK) {
+ ALOGW("Oneway call failed with error: %d", replyStatus);
+ }
+
+ LOG_RPC_DETAIL("Processed async transaction %" PRId64 " on %s", transaction->asyncNumber,
+ addr.toString().c_str());
+
+ // Check to see if there is another asynchronous transaction to process.
+ // This behavior differs from binder behavior, since in the binder
+ // driver, asynchronous transactions will be processed after existing
+ // pending binder transactions on the queue. The downside of this is
+ // that asynchronous transactions can be drowned out by synchronous
+ // transactions. However, we have no easy way to queue these
+ // transactions after the synchronous transactions we may want to read
+ // from the wire. So, in socket binder here, we have the opposite
+ // downside: asynchronous transactions may drown out synchronous
+ // transactions.
+ {
+ std::unique_lock<std::mutex> _l(mNodeMutex);
+ auto it = mNodeForAddress.find(addr);
+ // last refcount dropped after this transaction happened
+ if (it == mNodeForAddress.end()) return OK;
+
+ // note - only updated now, instead of later, so that other threads
+ // will queue any later transactions
+
+ // TODO(b/183140903): support > 2**64 async transactions
+ // (we can do this by allowing asyncNumber to wrap, since we
+ // don't expect more than 2**64 simultaneous transactions)
+ it->second.asyncNumber++;
+
+ if (it->second.asyncTodo.size() == 0) return OK;
+ if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+ LOG_RPC_DETAIL("Found next async transaction %" PRId64 " on %s",
+ it->second.asyncNumber, addr.toString().c_str());
+
+ // justification for const_cast (consider avoiding priority_queue):
+ // - AsyncTodo operator< doesn't depend on 'data' object
+ // - gotta go fast
+ std::vector<uint8_t> data = std::move(
+ const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
+ it->second.asyncTodo.pop();
+ _l.unlock();
+ return processTransactInternal(fd, connection, std::move(data));
+ }
+ }
+ return OK;
+ }
+
+ RpcWireReply rpcReply{
+ .status = replyStatus,
+ };
+
+ std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
+ memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
+ memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());
+
+ if (replyData.size() > std::numeric_limits<uint32_t>::max()) {
+ ALOGE("Reply size too big %zu", transactionData.size());
+ terminate();
+ return BAD_VALUE;
+ }
+
+ RpcWireHeader cmdReply{
+ .command = RPC_COMMAND_REPLY,
+ .bodySize = static_cast<uint32_t>(replyData.size()),
+ };
+
+ if (!rpcSend(fd, "reply header", &cmdReply, sizeof(RpcWireHeader))) {
+ return DEAD_OBJECT;
+ }
+ if (!rpcSend(fd, "reply body", replyData.data(), replyData.size())) {
+ return DEAD_OBJECT;
+ }
+ return OK;
+}
+
+status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
+ LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
+
+ std::vector<uint8_t> commandData(command.bodySize);
+ if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
+ return DEAD_OBJECT;
+ }
+
+ if (command.bodySize < sizeof(RpcWireAddress)) {
+ ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!",
+ sizeof(RpcWireAddress), command.bodySize);
+ terminate();
+ return BAD_VALUE;
+ }
+ RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data());
+
+ // TODO(b/182939933): heap allocation just for lookup
+ auto addr = RpcAddress::fromRawEmbedded(address);
+ std::unique_lock<std::mutex> _l(mNodeMutex);
+ auto it = mNodeForAddress.find(addr);
+ if (it == mNodeForAddress.end()) {
+ ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
+ dump();
+ return OK;
+ }
+
+ sp<IBinder> target = it->second.binder.promote();
+ if (target == nullptr) {
+ ALOGE("While requesting dec strong, binder has been deleted at address %s. Terminating!",
+ addr.toString().c_str());
+ terminate();
+ return BAD_VALUE;
+ }
+
+ if (it->second.timesSent == 0) {
+ ALOGE("No record of sending binder, but requested decStrong: %s", addr.toString().c_str());
+ return OK;
+ }
+
+ LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %s",
+ addr.toString().c_str());
+
+ sp<IBinder> tempHold;
+
+ it->second.timesSent--;
+ if (it->second.timesSent == 0) {
+ tempHold = it->second.sentRef;
+ it->second.sentRef = nullptr;
+
+ if (it->second.timesRecd == 0) {
+ mNodeForAddress.erase(it);
+ }
+ }
+
+ _l.unlock();
+ tempHold = nullptr; // destructor may make binder calls on this connection
+
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
new file mode 100644
index 0000000..f4f5151
--- /dev/null
+++ b/libs/binder/RpcState.h
@@ -0,0 +1,170 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcConnection.h>
+
+#include <map>
+#include <queue>
+
+namespace android {
+
+struct RpcWireHeader;
+
+/**
+ * Log a lot more information about RPC calls, when debugging issues. Usually,
+ * you would want to enable this in only one process. If repeated issues require
+ * a specific subset of logs to debug, this could be broken up like
+ * IPCThreadState's.
+ */
+#define SHOULD_LOG_RPC_DETAIL false
+
+#if SHOULD_LOG_RPC_DETAIL
+#define LOG_RPC_DETAIL(...) ALOGI(__VA_ARGS__)
+#else
+#define LOG_RPC_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
+#endif
+
+/**
+ * Abstracts away management of ref counts and the wire format from
+ * RpcConnection
+ */
+class RpcState {
+public:
+ RpcState();
+ ~RpcState();
+
+ sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
+
+ [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
+ uint32_t code, const Parcel& data,
+ const sp<RpcConnection>& connection, Parcel* reply,
+ uint32_t flags);
+ [[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
+ [[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection);
+
+ /**
+ * Called by Parcel for outgoing binders. This implies one refcount of
+ * ownership to the outgoing binder.
+ */
+ [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection,
+ const sp<IBinder>& binder, RpcAddress* outAddress);
+
+ /**
+ * Called by Parcel for incoming binders. This either returns the refcount
+ * to the process, if this process already has one, or it takes ownership of
+ * that refcount
+ */
+ sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address);
+
+ size_t countBinders();
+ void dump();
+
+private:
+ /**
+ * Called when reading or writing data to a connection fails to clean up
+ * data associated with the connection in order to cleanup binders.
+ * Specifically, we have a strong dependency cycle, since BpBinder is
+ * OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true).
+ *
+ * BpBinder -> RpcConnection -> RpcState
+ * ^-----------------------------/
+ *
+ * In the success case, eventually all refcounts should be propagated over
+ * the connection, though this could also be called to eagerly cleanup
+ * the connection.
+ *
+ * WARNING: RpcState is responsible for calling this when the connection is
+ * no longer recoverable.
+ */
+ void terminate();
+
+ [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
+ size_t size);
+ [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
+
+ [[nodiscard]] status_t waitForReply(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection, Parcel* reply);
+ [[nodiscard]] status_t processServerCommand(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection,
+ const RpcWireHeader& command);
+ [[nodiscard]] status_t processTransact(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection,
+ const RpcWireHeader& command);
+ [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
+ const sp<RpcConnection>& connection,
+ std::vector<uint8_t>&& transactionData);
+ [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
+ const RpcWireHeader& command);
+
+ struct BinderNode {
+ // Two cases:
+ // A - local binder we are serving
+ // B - remote binder, we are sending transactions to
+ wp<IBinder> binder;
+
+ // if timesSent > 0, this will be equal to binder.promote()
+ sp<IBinder> sentRef;
+
+ // Number of times we've sent this binder out of process, which
+ // translates to an implicit strong count. A client must send RPC binder
+ // socket's dec ref for each time it is sent out of process in order to
+ // deallocate it. Note, a proxy binder we are holding onto might be
+ // sent (this is important when the only remaining refcount of this
+ // binder is the one associated with a transaction sending it back to
+ // its server)
+ size_t timesSent = 0;
+
+ // Number of times we've received this binder, each time corresponds to
+ // a reference we hold over the wire (not a local incStrong/decStrong)
+ size_t timesRecd = 0;
+
+ // transaction ID, for async transactions
+ uint64_t asyncNumber = 0;
+
+ //
+ // CASE A - local binder we are serving
+ //
+
+ // async transaction queue, _only_ for local binder
+ struct AsyncTodo {
+ std::vector<uint8_t> data; // most convenient format, to move it here
+ uint64_t asyncNumber = 0;
+
+ bool operator<(const AsyncTodo& o) const {
+ return asyncNumber > /* !!! */ o.asyncNumber;
+ }
+ };
+ std::priority_queue<AsyncTodo> asyncTodo;
+
+ //
+ // CASE B - remote binder, we are sending transactions to
+ //
+
+ // (no additional data specific to remote binders)
+ };
+
+ std::mutex mNodeMutex;
+ bool mTerminated = false;
+ // binders known by both sides of a connection
+ std::map<RpcAddress, BinderNode> mNodeForAddress;
+};
+
+} // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
new file mode 100644
index 0000000..60ec6c9
--- /dev/null
+++ b/libs/binder/RpcWireFormat.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.
+ */
+#pragma once
+
+namespace android {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wpadded"
+
+enum : uint32_t {
+ /**
+ * follows is RpcWireTransaction, if flags != oneway, reply w/ RPC_COMMAND_REPLY expected
+ */
+ RPC_COMMAND_TRANSACT = 0,
+ /**
+ * follows is RpcWireReply
+ */
+ RPC_COMMAND_REPLY,
+ /**
+ * follows is RpcWireAddress
+ *
+ * note - this in the protocol directly instead of as a 'special
+ * transaction' in order to keep it as lightweight as possible (we don't
+ * want to create a 'Parcel' object for every decref)
+ */
+ RPC_COMMAND_DEC_STRONG,
+};
+
+/**
+ * These commands are used when the address in an RpcWireTransaction is zero'd
+ * out (no address). This allows the transact/reply flow to be used for
+ * additional server commands, without making the protocol for
+ * transactions/replies more complicated.
+ */
+enum : uint32_t {
+ RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
+};
+
+// serialization is like:
+// |RpcWireHeader|struct desginated by 'command'| (over and over again)
+
+struct RpcWireHeader {
+ uint32_t command; // RPC_COMMAND_*
+ uint32_t bodySize;
+
+ uint32_t reserved[2];
+};
+
+struct RpcWireAddress {
+ uint8_t address[32];
+};
+
+struct RpcWireTransaction {
+ RpcWireAddress address;
+ uint32_t code;
+ uint32_t flags;
+
+ uint64_t asyncNumber;
+
+ uint32_t reserved[4];
+
+ uint8_t data[0];
+};
+
+struct RpcWireReply {
+ int32_t status; // transact return
+ uint8_t data[0];
+};
+
+#pragma clang diagnostic pop
+
+} // namespace android
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index b56e09f..c3f1ba7 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -38,6 +38,18 @@
};
}
+void Stability::forceDowngradeCompilationUnit(const sp<IBinder>& binder) {
+ // Downgrading a remote binder would require also copying the version from
+ // the binder sent here. In practice though, we don't need to downgrade the
+ // stability of a remote binder, since this would as an effect only restrict
+ // what we can do to it.
+ LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder");
+
+ auto stability = Category::currentFromLevel(getLocalLevel());
+ status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
+}
+
std::string Stability::Category::debugString() {
return levelString(level) + " wire protocol version "
+ std::to_string(version);
@@ -45,13 +57,13 @@
void Stability::markCompilationUnit(IBinder* binder) {
auto stability = Category::currentFromLevel(getLocalLevel());
- status_t result = setRepr(binder, stability.repr(), true /*log*/);
+ status_t result = setRepr(binder, stability.repr(), REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
void Stability::markVintf(IBinder* binder) {
auto stability = Category::currentFromLevel(Level::VINTF);
- status_t result = setRepr(binder, stability.repr(), true /*log*/);
+ status_t result = setRepr(binder, stability.repr(), REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
@@ -62,7 +74,7 @@
void Stability::markVndk(IBinder* binder) {
auto stability = Category::currentFromLevel(Level::VENDOR);
- status_t result = setRepr(binder, stability.repr(), true /*log*/);
+ status_t result = setRepr(binder, stability.repr(), REPR_LOG);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
@@ -72,7 +84,7 @@
void Stability::tryMarkCompilationUnit(IBinder* binder) {
auto stability = Category::currentFromLevel(getLocalLevel());
- (void) setRepr(binder, stability.repr(), false /*log*/);
+ (void) setRepr(binder, stability.repr(), REPR_NONE);
}
Stability::Level Stability::getLocalLevel() {
@@ -88,7 +100,10 @@
#endif
}
-status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) {
+status_t Stability::setRepr(IBinder* binder, int32_t representation, uint32_t flags) {
+ bool log = flags & REPR_LOG;
+ bool allowDowngrade = flags & REPR_ALLOW_DOWNGRADE;
+
auto current = getCategory(binder);
auto setting = Category::fromRepr(representation);
@@ -123,7 +138,11 @@
return BAD_TYPE;
}
- if (current.repr() != 0 && current != setting) {
+ if (current == setting) return OK;
+
+ bool hasAlreadyBeenSet = current.repr() != 0;
+ bool isAllowedDowngrade = allowDowngrade && check(current, setting.level);
+ if (hasAlreadyBeenSet && !isAllowedDowngrade) {
if (log) {
ALOGE("Interface being set with %s but it is already marked as %s",
setting.debugString().c_str(),
@@ -132,7 +151,11 @@
return BAD_TYPE;
}
- if (current == setting) return OK;
+ if (isAllowedDowngrade) {
+ ALOGI("Interface set with %s downgraded to %s stability",
+ current.debugString().c_str(),
+ setting.debugString().c_str());
+ }
BBinder* local = binder->localBinder();
if (local != nullptr) {
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 1fbaa13..b58d919 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -25,6 +25,9 @@
"name": "binderLibTest"
},
{
+ "name": "binderRpcTest"
+ },
+ {
"name": "binderStabilityTest"
},
{
@@ -40,6 +43,9 @@
"name": "aidl_integration_test"
},
{
+ "name": "memunreachable_binder_test"
+ },
+ {
"name": "libbinderthreadstateutils_test"
},
{
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 684a7dc..a0ade50 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -16,7 +16,7 @@
#include <binder/TextOutput.h>
-#include <binder/Debug.h>
+#include "Debug.h"
#include <utils/String8.h>
#include <utils/String16.h>
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index a7a6563..c20d9f6 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -113,5 +113,14 @@
* Returns the debug flag for the given package.
* Unknown packages will cause the call to fail.
*/
- boolean isPackageDebuggable(in String packageName);
+ 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/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index f1085cf..eac1bb2 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -138,7 +138,12 @@
OP_RECORD_AUDIO_HOTWORD = 102,
// Ops 103-105 are currently unused in native, and intentionally omitted
OP_RECORD_AUDIO_OUTPUT = 106,
- _NUM_OP = 107
+ OP_SCHEDULE_EXACT_ALARM = 107,
+ OP_FINE_LOCATION_SOURCE = 108,
+ OP_COARSE_LOCATION_SOURCE = 109,
+ OP_MANAGE_MEDIA = 110,
+ OP_BLUETOOTH_CONNECT = 111,
+ _NUM_OP = 112
};
AppOpsManager();
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 22300ac..8ab7893 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -17,15 +17,19 @@
#pragma once
#include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
#include <utils/KeyedVector.h>
#include <utils/Mutex.h>
#include <utils/threads.h>
#include <unordered_map>
+#include <variant>
// ---------------------------------------------------------------------------
namespace android {
+class RpcConnection;
+class RpcState;
namespace internal {
class Stability;
}
@@ -37,6 +41,14 @@
{
public:
static BpBinder* create(int32_t handle);
+ static BpBinder* create(const sp<RpcConnection>& connection, const RpcAddress& address);
+
+ /**
+ * Return value:
+ * true - this is associated with a socket RpcConnection
+ * false - (usual) binder over e.g. /dev/binder
+ */
+ bool isRpcBinder() const;
virtual const String16& getInterfaceDescriptor() const;
virtual bool isBinderAlive() const;
@@ -108,33 +120,56 @@
KeyedVector<const void*, entry_t> mObjects;
};
- class PrivateAccessorForHandle {
+ class PrivateAccessorForId {
private:
- friend BpBinder;
- friend ::android::Parcel;
- friend ::android::ProcessState;
- explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {}
- int32_t handle() const { return mBinder->handle(); }
+ friend class BpBinder;
+ friend class ::android::Parcel;
+ friend class ::android::ProcessState;
+ friend class ::android::RpcState;
+ explicit PrivateAccessorForId(const BpBinder* binder) : mBinder(binder) {}
+
+ // valid if !isRpcBinder
+ int32_t binderHandle() const { return mBinder->binderHandle(); }
+
+ // valid if isRpcBinder
+ const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
+ const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); }
+
const BpBinder* mBinder;
};
- const PrivateAccessorForHandle getPrivateAccessorForHandle() const {
- return PrivateAccessorForHandle(this);
+ const PrivateAccessorForId getPrivateAccessorForId() const {
+ return PrivateAccessorForId(this);
}
private:
- friend PrivateAccessorForHandle;
+ friend PrivateAccessorForId;
- int32_t handle() const;
- BpBinder(int32_t handle,int32_t trackedUid);
+ struct BinderHandle {
+ int32_t handle;
+ };
+ struct SocketHandle {
+ sp<RpcConnection> connection;
+ RpcAddress address;
+ };
+ using Handle = std::variant<BinderHandle, SocketHandle>;
+
+ int32_t binderHandle() const;
+ const RpcAddress& rpcAddress() const;
+ const sp<RpcConnection>& rpcConnection() const;
+
+ explicit BpBinder(Handle&& handle);
+ BpBinder(BinderHandle&& handle, int32_t trackedUid);
+ explicit BpBinder(SocketHandle&& handle);
+
virtual ~BpBinder();
virtual void onFirstRef();
virtual void onLastStrongRef(const void* id);
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
friend ::android::internal::Stability;
- int32_t mStability;
- const int32_t mHandle;
+ int32_t mStability;
+ Handle mHandle;
struct Obituary {
wp<DeathRecipient> recipient;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 7b298f5..9578372 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -50,11 +50,14 @@
class IBinder;
class IPCThreadState;
class ProcessState;
+class RpcConnection;
class String8;
class TextOutput;
class Parcel {
friend class IPCThreadState;
+ friend class RpcState;
+
public:
class ReadableBlob;
class WritableBlob;
@@ -92,7 +95,21 @@
// In order to verify this, heap dumps should be used.
void markSensitive() const;
- // Writes the RPC header.
+ // For a 'data' Parcel, this should mark the Parcel as being prepared for a
+ // transaction on this specific binder object. Based on this, the format of
+ // the wire binder protocol may change (data is written differently when it
+ // is for an RPC transaction).
+ void markForBinder(const sp<IBinder>& binder);
+
+ // Whenever possible, markForBinder should be preferred. This method is
+ // called automatically on reply Parcels for RPC transactions.
+ void markForRpc(const sp<RpcConnection>& connection);
+
+ // Whether this Parcel is written for RPC transactions (after calls to
+ // markForBinder or markForRpc).
+ bool isForRpc() const;
+
+ // Writes the IPC/RPC header.
status_t writeInterfaceToken(const String16& interface);
status_t writeInterfaceToken(const char16_t* str, size_t len);
@@ -1106,6 +1123,7 @@
mutable bool mObjectsSorted;
mutable bool mRequestHeaderPresent;
+
mutable size_t mWorkSourceRequestHeaderPosition;
mutable bool mFdsKnown;
@@ -1118,8 +1136,7 @@
release_func mOwner;
- // TODO(167966510): reserved for binder/version/stability
- void* mReserved = reinterpret_cast<void*>(0xAAAAAAAA);
+ sp<RpcConnection> mConnection;
class Blob {
public:
diff --git a/libs/binder/include/binder/RpcAddress.h b/libs/binder/include/binder/RpcAddress.h
new file mode 100644
index 0000000..5a3f3a6
--- /dev/null
+++ b/libs/binder/include/binder/RpcAddress.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <memory>
+
+#include <utils/Errors.h>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+struct RpcWireAddress;
+
+/**
+ * This class represents an identifier of a binder object.
+ *
+ * The purpose of this class it to hide the ABI of an RpcWireAddress, and
+ * potentially allow us to change the size of it in the future (RpcWireAddress
+ * is PIMPL, essentially - although the type that is used here is not exposed).
+ */
+class RpcAddress {
+public:
+ /**
+ * The zero address is used for special RPC transactions, but it might also
+ * be used in conjunction with readFromParcel.
+ */
+ static RpcAddress zero();
+
+ bool isZero() const;
+
+ /**
+ * Create a new address which is unique
+ */
+ static RpcAddress unique();
+
+ /**
+ * Creates a new address as a copy of an embedded object.
+ */
+ static RpcAddress fromRawEmbedded(const RpcWireAddress* raw);
+ const RpcWireAddress& viewRawEmbedded() const;
+
+ bool operator<(const RpcAddress& rhs) const;
+ std::string toString() const;
+
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(const Parcel& parcel);
+
+ ~RpcAddress();
+
+private:
+ RpcAddress();
+
+ std::shared_ptr<RpcWireAddress> mRawAddr;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
new file mode 100644
index 0000000..65c5232
--- /dev/null
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -0,0 +1,158 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <optional>
+#include <vector>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+class RpcServer;
+class RpcState;
+
+/**
+ * This represents a multi-threaded/multi-socket connection between a client
+ * and a server.
+ */
+class RpcConnection final : public virtual RefBase {
+public:
+ static sp<RpcConnection> make();
+
+ /**
+ * This represents a connection for responses, e.g.:
+ *
+ * process A serves binder a
+ * process B opens a connection to process A
+ * process B makes binder b and sends it to A
+ * A uses this 'back connection' to send things back to B
+ *
+ * This should be called once, and then a call should be made to join per
+ * connection thread.
+ */
+ [[nodiscard]] bool setupUnixDomainServer(const char* path);
+
+ /**
+ * This should be called once per thread, matching 'join' in the remote
+ * process.
+ */
+ [[nodiscard]] bool addUnixDomainClient(const char* path);
+
+ /**
+ * Query the other side of the connection for the root object hosted by that
+ * process's RpcServer (if one exists)
+ */
+ sp<IBinder> getRootObject();
+
+ [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+ [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+ /**
+ * Adds a server thread accepting connections. Must be called after
+ * setup*Server.
+ */
+ void join();
+
+ ~RpcConnection();
+
+ void setForServer(const wp<RpcServer>& server);
+ wp<RpcServer> server();
+
+ // internal only
+ const std::unique_ptr<RpcState>& state() { return mState; }
+
+private:
+ RpcConnection();
+
+ void addServer(base::unique_fd&& fd);
+ void addClient(base::unique_fd&& fd);
+
+ struct ConnectionSocket : public RefBase {
+ base::unique_fd fd;
+
+ // whether this or another thread is currently using this fd to make
+ // or receive transactions.
+ std::optional<pid_t> exclusiveTid;
+ };
+
+ enum class SocketUse {
+ CLIENT,
+ CLIENT_ASYNC,
+ CLIENT_REFCOUNT,
+ SERVER,
+ };
+
+ // RAII object for connection socket
+ class ExclusiveSocket {
+ public:
+ explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use);
+ ~ExclusiveSocket();
+ const base::unique_fd& fd() { return mSocket->fd; }
+
+ private:
+ static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
+ sp<ConnectionSocket>* available,
+ std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint);
+
+ sp<RpcConnection> mConnection; // avoid deallocation
+ sp<ConnectionSocket> mSocket;
+
+ // whether this is being used for a nested transaction (being on the same
+ // thread guarantees we won't write in the middle of a message, the way
+ // the wire protocol is constructed guarantees this is safe).
+ bool mReentrant = false;
+ };
+
+ // On the other side of a connection, for each of mClients here, there should
+ // be one of mServers on the other side (and vice versa).
+ //
+ // For the simplest connection, a single server with one client, you would
+ // have:
+ // - the server has a single 'mServers' and a thread listening on this
+ // - the client has a single 'mClients' and makes calls to this
+ // - here, when the client makes a call, the server can call back into it
+ // (nested calls), but outside of this, the client will only ever read
+ // calls from the server when it makes a call itself.
+ //
+ // For a more complicated case, the client might itself open up a thread to
+ // serve calls to the server at all times (e.g. if it hosts a callback)
+
+ wp<RpcServer> mForServer; // maybe null, for client connections
+
+ std::unique_ptr<RpcState> mState;
+
+ base::unique_fd mServer; // socket we are accepting connections on
+
+ std::mutex mSocketMutex; // for all below
+ std::condition_variable mSocketCv; // for mWaitingThreads
+ size_t mWaitingThreads = 0;
+ size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
+ std::vector<sp<ConnectionSocket>> mClients;
+ std::vector<sp<ConnectionSocket>> mServers;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
new file mode 100644
index 0000000..a2c2aee
--- /dev/null
+++ b/libs/binder/include/binder/RpcServer.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcConnection.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+/**
+ * This represents a server of an interface, which may be connected to by any
+ * number of clients over sockets.
+ *
+ * This object is not (currently) thread safe. All calls to it are expected to
+ * happen at process startup.
+ */
+class RpcServer final : public virtual RefBase {
+public:
+ static sp<RpcServer> make();
+
+ void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+ /**
+ * Setup a static connection, when the number of clients are known.
+ *
+ * Each call to this function corresponds to a different client, and clients
+ * each have their own threadpools.
+ *
+ * TODO(b/167966510): support dynamic creation of connections/threads
+ */
+ sp<RpcConnection> addClientConnection();
+
+ /**
+ * Allowing a server to explicitly drop clients would be easy to add here,
+ * but it is not currently implemented, since users of this functionality
+ * could not use similar functionality if they are running under real
+ * binder.
+ */
+ // void drop(const sp<RpcConnection>& connection);
+
+ /**
+ * The root object can be retrieved by any client, without any
+ * authentication.
+ */
+ void setRootObject(const sp<IBinder>& binder);
+
+ /**
+ * Root object set with setRootObject
+ */
+ sp<IBinder> getRootObject();
+
+ ~RpcServer();
+
+private:
+ RpcServer();
+
+ bool mAgreedExperimental = false;
+
+ sp<IBinder> mRootObject;
+
+ std::vector<sp<RpcConnection>> mConnections; // per-client
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 12272ba..a09e587 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -49,10 +49,17 @@
// that it knows how to process. The summary of stability of a binder is
// represented by a Stability::Category object.
-// WARNING: These APIs are only ever expected to be called by auto-generated code.
-// Instead of calling them, you should set the stability of a .aidl interface
class Stability final {
public:
+ // Given a binder interface at a certain stability, there may be some
+ // requirements associated with that higher stability level. For instance, a
+ // VINTF stability binder is required to be in the VINTF manifest. This API
+ // can be called to use that same interface within a partition.
+ static void forceDowngradeCompilationUnit(const sp<IBinder>& binder);
+
+ // WARNING: Below APIs are only ever expected to be called by auto-generated code.
+ // Instead of calling them, you should set the stability of a .aidl interface
+
// WARNING: This is only ever expected to be called by auto-generated code. You likely want to
// change or modify the stability class of the interface you are using.
// This must be called as soon as the binder in question is constructed. No thread safety
@@ -139,9 +146,14 @@
// returns the stability according to how this was built
static Level getLocalLevel();
+ enum {
+ REPR_NONE = 0,
+ REPR_LOG = 1,
+ REPR_ALLOW_DOWNGRADE = 2,
+ };
// applies stability to binder if stability level is known
__attribute__((warn_unused_result))
- static status_t setRepr(IBinder* binder, int32_t representation, bool log);
+ static status_t setRepr(IBinder* binder, int32_t representation, uint32_t flags);
// get stability information as encoded on the wire
static Category getCategory(IBinder* binder);
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 05eb64b..6c44726 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -82,7 +82,10 @@
*/
template <class T, class... Args>
static std::shared_ptr<T> make(Args&&... args) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
T* t = new T(std::forward<Args>(args)...);
+#pragma clang diagnostic pop
// warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
return t->template ref<T>(); // NOLINT(clang-analyzer-unix.Malloc)
}
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 5842925..ef71a81 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -136,8 +136,7 @@
template <typename _U>
static std::enable_if_t<
#ifdef HAS_NDK_INTERFACE
- std::is_base_of_v<::ndk::ICInterface, _U> || std::is_same_v<::ndk::SpAIBinder, _U> ||
- std::is_same_v<::ndk::ScopedFileDescriptor, _U> ||
+ std::is_base_of_v<::ndk::ICInterface, _U> ||
std::is_same_v<::ndk::AParcelableHolder, _U>
#else
std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
@@ -168,13 +167,19 @@
return std::to_string(t);
} else if constexpr (std::is_same_v<std::string, _T>) {
return t;
+#ifdef HAS_NDK_INTERFACE
+ } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
+ return (t.get() == nullptr) ? "(null)" : "";
+ } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
+ return (t.get() == -1) ? "(null)" : "";
+#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
std::stringstream out;
out << t;
return out.str();
#endif
- } else if constexpr (details::IsPointerLike<_T>::value) {
+ } else if constexpr (details::IsPointerLike<_T>::value || std::is_pointer_v<_T>) {
if (!t) return "(null)";
std::stringstream out;
out << ToString(*t);
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index a44c261..8941e49 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -36,11 +36,6 @@
__BEGIN_DECLS
-#ifndef __ANDROID_API__
-#error Android builds must be compiled against a specific API. If this is an \
- android platform host build, you must use libbinder_ndk_host_user.
-#endif
-
typedef uint32_t binder_flags_t;
enum {
/**
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 05b25e7..b4dc08a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -32,6 +32,16 @@
__BEGIN_DECLS
+#ifndef __ANDROID_API__
+#error Android builds must be compiled against a specific API. If this is an \
+ android platform host build, you must use libbinder_ndk_host_user.
+#endif
+
+/**
+ * Low-level status types for use in binder. This is the least preferable way to
+ * return an error for binder services (where binder_exception_t should be used,
+ * particularly EX_SERVICE_SPECIFIC).
+ */
enum {
STATUS_OK = 0,
@@ -62,6 +72,10 @@
*/
typedef int32_t binder_status_t;
+/**
+ * Top level exceptions types for Android binder errors, mapping to Java
+ * exceptions. Also see Parcel.java.
+ */
enum {
EX_NONE = 0,
EX_SECURITY = -1,
@@ -170,7 +184,8 @@
/**
* New status with binder_status_t. This is typically for low level failures when a binder_status_t
* is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning
- * an AStatus instance.
+ * an AStatus instance. This is the least preferable way to return errors.
+ * Prefer exceptions (particularly service-specific errors) when possible.
*
* Available since API level 29.
*
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 5df0012..0668472 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -26,6 +26,9 @@
* This registers the service with the default service manager under this instance name. This does
* not take ownership of binder.
*
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
* \param binder object to register globally with the service manager.
* \param instance identifier of the service. This will be used to lookup the service.
*
@@ -39,6 +42,9 @@
* service is not available This also implicitly calls AIBinder_incStrong (so the caller of this
* function is responsible for calling AIBinder_decStrong).
*
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
* \param instance identifier of the service used to lookup the service.
*/
__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
@@ -48,6 +54,9 @@
* it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
* for calling AIBinder_decStrong).
*
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
* \param instance identifier of the service used to lookup the service.
*/
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
@@ -78,6 +87,9 @@
* This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
* for calling AIBinder_decStrong).
*
+ * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * AIDL services. TODO(b/139325195)
+ *
* \param instance identifier of the service used to lookup the service.
*
* \return service if registered, null if not.
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index d54c1a1..6372449 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -20,9 +20,7 @@
__BEGIN_DECLS
-#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
-#error this is only for platform code
-#endif
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
/**
* Gets whether or not FDs are allowed by this AParcel
@@ -33,6 +31,9 @@
*/
bool AParcel_getAllowFds(const AParcel*);
+#endif
+
+#if !defined(__ANDROID_APEX__)
/**
* Data written to the parcel will be zero'd before being deleted or realloced.
*
@@ -43,5 +44,6 @@
* \param parcel The parcel to clear associated data from.
*/
void AParcel_markSensitive(const AParcel* parcel);
+#endif
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 8d08275..f1db653 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -117,6 +117,7 @@
ABinderProcess_setupPolling; # apex
AIBinder_getCallingSid; # apex
AIBinder_setRequestingSid; # apex
+ AParcel_markSensitive; # llndk
AServiceManager_isDeclared; # apex llndk
AServiceManager_forEachDeclaredInstance; # apex llndk
AServiceManager_registerLazyService; # llndk
@@ -139,7 +140,6 @@
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
- AParcel_markSensitive;
extern "C++" {
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index d53a88f..321b422 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -33,13 +33,13 @@
/// Binder action to perform.
///
-/// This must be a number between [`IBinder::FIRST_CALL_TRANSACTION`] and
-/// [`IBinder::LAST_CALL_TRANSACTION`].
+/// This must be a number between [`FIRST_CALL_TRANSACTION`] and
+/// [`LAST_CALL_TRANSACTION`].
pub type TransactionCode = u32;
/// Additional operation flags.
///
-/// `IBinder::FLAG_*` values.
+/// `FLAG_*` values.
pub type TransactionFlags = u32;
/// Super-trait for Binder interfaces.
@@ -56,6 +56,26 @@
}
}
+/// Interface stability promise
+///
+/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
+/// makes no stability guarantees ([`Local`]). [`Local`] is
+/// currently the default stability.
+pub enum Stability {
+ /// Default stability, visible to other modules in the same compilation
+ /// context (e.g. modules on system.img)
+ Local,
+
+ /// A Vendor Interface Object, which promises to be stable
+ Vintf,
+}
+
+impl Default for Stability {
+ fn default() -> Self {
+ Stability::Local
+ }
+}
+
/// A local service that can be remotable via Binder.
///
/// An object that implement this interface made be made into a Binder service
@@ -85,20 +105,24 @@
fn get_class() -> InterfaceClass;
}
-/// Interface of binder local or remote objects.
+/// First transaction code available for user commands (inclusive)
+pub const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
+/// Last transaction code available for user commands (inclusive)
+pub const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
+
+/// Corresponds to TF_ONE_WAY -- an asynchronous call.
+pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+/// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
+/// Set to the vendor flag if we are building for the VNDK, 0 otherwise
+pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL;
+
+/// Internal interface of binder local or remote objects for making
+/// transactions.
///
-/// This trait corresponds to the interface of the C++ `IBinder` class.
-pub trait IBinder {
- /// First transaction code available for user commands (inclusive)
- const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
- /// Last transaction code available for user commands (inclusive)
- const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
-
- /// Corresponds to TF_ONE_WAY -- an asynchronous call.
- const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
- /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
- const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
-
+/// This trait corresponds to the parts of the interface of the C++ `IBinder`
+/// class which are internal implementation details.
+pub trait IBinderInternal: IBinder {
/// Is this object still alive?
fn is_binder_alive(&self) -> bool;
@@ -122,19 +146,24 @@
/// * `data` - [`Parcel`] with input data
/// * `reply` - Optional [`Parcel`] for reply data
/// * `flags` - Transaction flags, e.g. marking the transaction as
- /// asynchronous ([`FLAG_ONEWAY`](IBinder::FLAG_ONEWAY))
+ /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
&self,
code: TransactionCode,
flags: TransactionFlags,
input_callback: F,
) -> Result<Parcel>;
+}
+/// Interface of binder local or remote objects.
+///
+/// This trait corresponds to the parts of the interface of the C++ `IBinder`
+/// class which are public.
+pub trait IBinder {
/// Register the recipient for a notification if this binder
/// goes away. If this binder object unexpectedly goes away
/// (typically because its hosting process has been killed),
- /// then DeathRecipient::binder_died() will be called with a reference
- /// to this.
+ /// then the `DeathRecipient`'s callback will be called.
///
/// You will only receive death notifications for remote binders,
/// as local binders by definition can't die without you dying as well.
@@ -142,11 +171,6 @@
/// INVALID_OPERATION code being returned and nothing happening.
///
/// This link always holds a weak reference to its recipient.
- ///
- /// You will only receive a weak reference to the dead
- /// binder. You should not try to promote this to a strong reference.
- /// (Nor should you need to, as there is nothing useful you can
- /// directly do with it now that it has passed on.)
fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
/// Remove a previously registered death notification.
@@ -222,7 +246,8 @@
// the number of u16 elements before the null terminator.
let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0);
- CStr::from_ptr(raw_descriptor).to_str()
+ CStr::from_ptr(raw_descriptor)
+ .to_str()
.expect("Expected valid UTF-8 string from AIBinder_Class_getDescriptor")
.into()
}
@@ -599,6 +624,23 @@
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ stability: $crate::Stability::default(),
+ }
+ }
+ };
+
+ {
+ $interface:path[$descriptor:expr] {
+ native: $native:ident($on_transact:path),
+ proxy: $proxy:ident,
+ stability: $stability:expr,
+ }
+ } => {
+ $crate::declare_binder_interface! {
+ $interface[$descriptor] {
+ native: $native($on_transact),
+ proxy: $proxy {},
+ stability: $stability,
}
}
};
@@ -613,12 +655,33 @@
} => {
$crate::declare_binder_interface! {
$interface[$descriptor] {
+ native: $native($on_transact),
+ proxy: $proxy {
+ $($fname: $fty = $finit),*
+ },
+ stability: $crate::Stability::default(),
+ }
+ }
+ };
+
+ {
+ $interface:path[$descriptor:expr] {
+ native: $native:ident($on_transact:path),
+ proxy: $proxy:ident {
+ $($fname:ident: $fty:ty = $finit:expr),*
+ },
+ stability: $stability:expr,
+ }
+ } => {
+ $crate::declare_binder_interface! {
+ $interface[$descriptor] {
@doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
native: $native($on_transact),
@doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ stability: $stability,
}
}
};
@@ -632,6 +695,8 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+
+ stability: $stability:expr,
}
} => {
#[doc = $proxy_doc]
@@ -666,7 +731,7 @@
impl $native {
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
- let binder = $crate::Binder::new($native(Box::new(inner)));
+ let binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
$crate::Strong::new(Box::new(binder))
}
}
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index 4492cf7..2598ebc 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -77,9 +77,7 @@
e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
- e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => {
- ExceptionCode::NETWORK_MAIN_THREAD
- }
+ e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => ExceptionCode::NETWORK_MAIN_THREAD,
e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
ExceptionCode::UNSUPPORTED_OPERATION
}
@@ -96,6 +94,16 @@
/// Used in AIDL transactions to represent failed transactions.
pub struct Status(*mut sys::AStatus);
+// Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
+// duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
+// in Rust, and the NDK API says we're the owner of our `AStatus` objects so outside code should not
+// be mutating them underneath us.
+unsafe impl Sync for Status {}
+
+// Safety: `Status` always contains an owning pointer to a global, immutable, interned `AStatus`.
+// A thread-local `AStatus` would not be valid.
+unsafe impl Send for Status {}
+
impl Status {
/// Create a status object representing a successful transaction.
pub fn ok() -> Self {
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 43a237a..30928a5 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,8 +107,10 @@
pub mod parcel;
pub use crate::binder::{
- FromIBinder, IBinder, Interface, InterfaceClass, Remotable, Strong, TransactionCode,
- TransactionFlags, Weak,
+ FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
+ Stability, Strong, TransactionCode, TransactionFlags, Weak,
+ FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
+ LAST_CALL_TRANSACTION,
};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
pub use native::add_service;
@@ -123,8 +125,8 @@
pub use super::parcel::ParcelFileDescriptor;
pub use super::{add_service, get_interface};
pub use super::{
- ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState,
- Weak, WpIBinder,
+ DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder, Status,
+ StatusCode, Strong, ThreadState, Weak, WpIBinder,
};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 185645e..3b3fd08 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode};
use crate::error::{status_result, status_t, Result, StatusCode};
use crate::parcel::{Parcel, Serialize};
use crate::proxy::SpIBinder;
@@ -49,11 +49,19 @@
unsafe impl<T: Remotable> Send for Binder<T> {}
impl<T: Remotable> Binder<T> {
- /// Create a new Binder remotable object.
+ /// Create a new Binder remotable object with default stability
///
/// This moves the `rust_object` into an owned [`Box`] and Binder will
/// manage its lifetime.
pub fn new(rust_object: T) -> Binder<T> {
+ Self::new_with_stability(rust_object, Stability::default())
+ }
+
+ /// Create a new Binder remotable object with the given stability
+ ///
+ /// This moves the `rust_object` into an owned [`Box`] and Binder will
+ /// manage its lifetime.
+ pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
let class = T::get_class();
let rust_object = Box::into_raw(Box::new(rust_object));
let ibinder = unsafe {
@@ -65,10 +73,12 @@
// ends.
sys::AIBinder_new(class.into(), rust_object as *mut c_void)
};
- Binder {
+ let mut binder = Binder {
ibinder,
rust_object,
- }
+ };
+ binder.mark_stability(stability);
+ binder
}
/// Set the extension of a binder interface. This allows a downstream
@@ -161,6 +171,42 @@
pub fn get_descriptor() -> &'static str {
T::get_descriptor()
}
+
+ /// Mark this binder object with the given stability guarantee
+ fn mark_stability(&mut self, stability: Stability) {
+ match stability {
+ Stability::Local => self.mark_local_stability(),
+ Stability::Vintf => {
+ unsafe {
+ // Safety: Self always contains a valid `AIBinder` pointer, so
+ // we can always call this C API safely.
+ sys::AIBinder_markVintfStability(self.as_native_mut());
+ }
+ }
+ }
+ }
+
+ /// Mark this binder object with local stability, which is vendor if we are
+ /// building for the VNDK and system otherwise.
+ #[cfg(vendor_ndk)]
+ fn mark_local_stability(&mut self) {
+ unsafe {
+ // Safety: Self always contains a valid `AIBinder` pointer, so
+ // we can always call this C API safely.
+ sys::AIBinder_markVendorStability(self.as_native_mut());
+ }
+ }
+
+ /// Mark this binder object with local stability, which is vendor if we are
+ /// building for the VNDK and system otherwise.
+ #[cfg(not(vendor_ndk))]
+ fn mark_local_stability(&mut self) {
+ unsafe {
+ // Safety: Self always contains a valid `AIBinder` pointer, so
+ // we can always call this C API safely.
+ sys::AIBinder_markSystemStability(self.as_native_mut());
+ }
+ }
}
impl<T: Remotable> Interface for Binder<T> {
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 132e075..52036f5 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -17,7 +17,8 @@
//! Rust API for interacting with a remote binder service.
use crate::binder::{
- AsNative, FromIBinder, IBinder, Interface, InterfaceClass, Strong, TransactionCode, TransactionFlags,
+ AsNative, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Strong,
+ TransactionCode, TransactionFlags,
};
use crate::error::{status_result, Result, StatusCode};
use crate::parcel::{
@@ -26,8 +27,8 @@
};
use crate::sys;
-use std::convert::TryInto;
use std::cmp::Ordering;
+use std::convert::TryInto;
use std::ffi::{c_void, CString};
use std::fmt;
use std::os::unix::io::AsRawFd;
@@ -211,7 +212,7 @@
}
}
-impl<T: AsNative<sys::AIBinder>> IBinder for T {
+impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
/// Perform a binder transaction
fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
&self,
@@ -300,9 +301,7 @@
}
fn set_requesting_sid(&mut self, enable: bool) {
- unsafe {
- sys::AIBinder_setRequestingSid(self.as_native_mut(), enable)
- };
+ unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
}
fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
@@ -351,7 +350,9 @@
status_result(status)?;
Ok(ibinder)
}
+}
+impl<T: AsNative<sys::AIBinder>> IBinder for T {
fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
status_result(unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -472,7 +473,10 @@
// WpIBinder object from it.
sys::AIBinder_Weak_clone(self.0)
};
- assert!(!ptr.is_null(), "Unexpected null pointer from AIBinder_Weak_clone");
+ assert!(
+ !ptr.is_null(),
+ "Unexpected null pointer from AIBinder_Weak_clone"
+ );
Self(ptr)
}
}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index ef142b5..65fa2ca 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -21,6 +21,7 @@
#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
#include <android/binder_shell.h>
+#include <android/binder_stability.h>
#include <android/binder_status.h>
namespace android {
@@ -80,6 +81,7 @@
enum {
FLAG_ONEWAY = FLAG_ONEWAY,
FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
+ FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL,
};
} // namespace consts
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 719229c..60e3502 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -18,7 +18,10 @@
use binder::declare_binder_interface;
use binder::parcel::Parcel;
-use binder::{Binder, IBinder, Interface, SpIBinder, StatusCode, ThreadState, TransactionCode};
+use binder::{
+ Binder, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
+ FIRST_CALL_TRANSACTION,
+};
use std::convert::{TryFrom, TryInto};
/// Name of service runner.
@@ -83,7 +86,7 @@
#[repr(u32)]
enum TestTransactionCode {
- Test = SpIBinder::FIRST_CALL_TRANSACTION,
+ Test = FIRST_CALL_TRANSACTION,
GetSelinuxContext,
}
@@ -196,7 +199,6 @@
impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {}
-
#[cfg(test)]
mod tests {
use selinux_bindgen as selinux_sys;
@@ -209,9 +211,12 @@
use std::thread;
use std::time::Duration;
- use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, Strong};
+ use binder::{
+ Binder, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface, SpIBinder,
+ StatusCode, Strong,
+ };
- use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService};
+ use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
pub struct ScopedServiceProcess(Child);
@@ -290,7 +295,9 @@
};
assert_eq!(
test_client.get_selinux_context().unwrap(),
- expected_context.to_str().expect("context was invalid UTF-8"),
+ expected_context
+ .to_str()
+ .expect("context was invalid UTF-8"),
);
}
@@ -479,18 +486,22 @@
// This should succeed although we will have to treat the service as
// remote.
- let _interface: Strong<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
- .expect("Could not re-interpret service as the ITestSameDescriptor interface");
+ let _interface: Strong<dyn ITestSameDescriptor> =
+ FromIBinder::try_from(service.as_binder())
+ .expect("Could not re-interpret service as the ITestSameDescriptor interface");
}
/// Test that we can round-trip a rust service through a generic IBinder
#[test]
fn reassociate_rust_binder() {
let service_name = "testing_service";
- let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() })
- .as_binder();
+ let service_ibinder = BnTest::new_binder(TestService {
+ s: service_name.to_string(),
+ })
+ .as_binder();
- let service: Strong<dyn ITest> = service_ibinder.into_interface()
+ let service: Strong<dyn ITest> = service_ibinder
+ .into_interface()
.expect("Could not reassociate the generic ibinder");
assert_eq!(service.test().unwrap(), service_name);
@@ -499,7 +510,9 @@
#[test]
fn weak_binder_upgrade() {
let service_name = "testing_service";
- let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+ let service = BnTest::new_binder(TestService {
+ s: service_name.to_string(),
+ });
let weak = Strong::downgrade(&service);
@@ -512,7 +525,9 @@
fn weak_binder_upgrade_dead() {
let service_name = "testing_service";
let weak = {
- let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+ let service = BnTest::new_binder(TestService {
+ s: service_name.to_string(),
+ });
Strong::downgrade(&service)
};
@@ -523,7 +538,9 @@
#[test]
fn weak_binder_clone() {
let service_name = "testing_service";
- let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+ let service = BnTest::new_binder(TestService {
+ s: service_name.to_string(),
+ });
let weak = Strong::downgrade(&service);
let cloned = weak.clone();
@@ -539,8 +556,12 @@
#[test]
#[allow(clippy::eq_op)]
fn binder_ord() {
- let service1 = BnTest::new_binder(TestService { s: "testing_service1".to_string() });
- let service2 = BnTest::new_binder(TestService { s: "testing_service2".to_string() });
+ let service1 = BnTest::new_binder(TestService {
+ s: "testing_service1".to_string(),
+ });
+ let service2 = BnTest::new_binder(TestService {
+ s: "testing_service2".to_string(),
+ });
assert!(!(service1 < service1));
assert!(!(service1 > service1));
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2bf3d03..f1b068e 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -40,6 +40,41 @@
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
+macro_rules! assert_eq {
+ ($left:expr, $right:expr $(,)?) => {
+ match (&$left, &$right) {
+ (left, right) => {
+ if *left != *right {
+ eprintln!(
+ "assertion failed: `{:?}` == `{:?}`, {}:{}:{}",
+ &*left,
+ &*right,
+ file!(),
+ line!(),
+ column!()
+ );
+ return Err(StatusCode::FAILED_TRANSACTION);
+ }
+ }
+ }
+ };
+}
+
+macro_rules! assert {
+ ($expr:expr) => {
+ if !$expr {
+ eprintln!(
+ "assertion failed: `{:?}`, {}:{}:{}",
+ $expr,
+ file!(),
+ line!(),
+ column!()
+ );
+ return Err(StatusCode::FAILED_TRANSACTION);
+ }
+ };
+}
+
static SERVICE_ONCE: Once = Once::new();
static mut SERVICE: Option<SpIBinder> = None;
@@ -282,7 +317,7 @@
))?;
}
bindings::Transaction_TEST_FAIL => {
- return Err(StatusCode::FAILED_TRANSACTION)
+ assert!(false);
}
_ => return Err(StatusCode::UNKNOWN_TRANSACTION),
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 3bbb0b5..a44cddf 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -105,6 +105,26 @@
}
cc_test {
+ name: "binderRpcTest",
+ defaults: ["binder_test_defaults"],
+
+ srcs: [
+ "IBinderRpcSession.aidl",
+ "IBinderRpcTest.aidl",
+ "binderRpcTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbase",
+ "libutils",
+ "libcutils",
+ "liblog",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
+
+cc_test {
name: "binderThroughputTest",
defaults: ["binder_test_defaults"],
srcs: ["binderThroughputTest.cpp"],
diff --git a/libs/binder/tests/IBinderRpcSession.aidl b/libs/binder/tests/IBinderRpcSession.aidl
new file mode 100644
index 0000000..cf5f318
--- /dev/null
+++ b/libs/binder/tests/IBinderRpcSession.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+interface IBinderRpcSession {
+ @utf8InCpp String getName();
+}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
new file mode 100644
index 0000000..2bdb264
--- /dev/null
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+interface IBinderRpcTest {
+ oneway void sendString(@utf8InCpp String str);
+ @utf8InCpp String doubleString(@utf8InCpp String str);
+
+ // number of known RPC binders to process, RpcState::countBinders
+ int countBinders();
+
+ // Caller sends server, callee pings caller's server and returns error code.
+ int pingMe(IBinder binder);
+ @nullable IBinder repeatBinder(@nullable IBinder binder);
+
+ void holdBinder(@nullable IBinder binder);
+ @nullable IBinder getHeldBinder();
+
+ // Idea is client creates its own instance of IBinderRpcTest and calls this,
+ // and the server calls 'binder' with (calls - 1) passing itself as 'binder',
+ // going back and forth until calls = 0
+ void nestMe(IBinderRpcTest binder, int calls);
+
+ // should always return the same binder
+ IBinder alwaysGiveMeTheSameBinder();
+
+ // Idea is that the server will not hold onto the session, the remote connection
+ // object must. This is to test lifetimes of binder objects, and consequently, also
+ // identity (since by assigning sessions names, we can make sure a section always
+ // references the session it was originally opened with).
+ IBinderRpcSession openSession(@utf8InCpp String name);
+
+ // Decremented in ~IBinderRpcSession
+ int getNumOpenSessions();
+
+ // primitives to test threading behavior
+ void lock();
+ oneway void unlockInMsAsync(int ms);
+ void lockUnlock(); // locks and unlocks a mutex
+
+ // take up binder thread for some time
+ void sleepMs(int ms);
+ oneway void sleepMsAsync(int ms);
+
+ void die(boolean cleanup);
+}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
new file mode 100644
index 0000000..6fa5333
--- /dev/null
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -0,0 +1,762 @@
+/*
+ * 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 <sys/prctl.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+#include <BnBinderRpcSession.h>
+#include <BnBinderRpcTest.h>
+#include <android-base/logging.h>
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/RpcConnection.h>
+#include <binder/RpcServer.h>
+#include <gtest/gtest.h>
+
+#include "../RpcState.h" // for debugging
+
+namespace android {
+
+using android::binder::Status;
+
+#define EXPECT_OK(status) \
+ do { \
+ Status stat = (status); \
+ EXPECT_TRUE(stat.isOk()) << stat; \
+ } while (false)
+
+class MyBinderRpcSession : public BnBinderRpcSession {
+public:
+ static std::atomic<int32_t> gNum;
+
+ MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; }
+ Status getName(std::string* name) override {
+ *name = mName;
+ return Status::ok();
+ }
+ ~MyBinderRpcSession() { gNum--; }
+
+private:
+ std::string mName;
+};
+std::atomic<int32_t> MyBinderRpcSession::gNum;
+
+class MyBinderRpcTest : public BnBinderRpcTest {
+public:
+ sp<RpcConnection> connection;
+
+ Status sendString(const std::string& str) override {
+ std::cout << "Child received string: " << str << std::endl;
+ return Status::ok();
+ }
+ Status doubleString(const std::string& str, std::string* strstr) override {
+ std::cout << "Child received string to double: " << str << std::endl;
+ *strstr = str + str;
+ return Status::ok();
+ }
+ Status countBinders(int32_t* out) override {
+ if (connection == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ *out = connection->state()->countBinders();
+ if (*out != 1) {
+ connection->state()->dump();
+ }
+ return Status::ok();
+ }
+ Status pingMe(const sp<IBinder>& binder, int32_t* out) override {
+ if (binder == nullptr) {
+ std::cout << "Received null binder!" << std::endl;
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ *out = binder->pingBinder();
+ return Status::ok();
+ }
+ Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override {
+ *out = binder;
+ return Status::ok();
+ }
+ static sp<IBinder> mHeldBinder;
+ Status holdBinder(const sp<IBinder>& binder) override {
+ mHeldBinder = binder;
+ return Status::ok();
+ }
+ Status getHeldBinder(sp<IBinder>* held) override {
+ *held = mHeldBinder;
+ return Status::ok();
+ }
+ Status nestMe(const sp<IBinderRpcTest>& binder, int count) override {
+ if (count <= 0) return Status::ok();
+ return binder->nestMe(this, count - 1);
+ }
+ Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override {
+ static sp<IBinder> binder = new BBinder;
+ *out = binder;
+ return Status::ok();
+ }
+ Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override {
+ *out = new MyBinderRpcSession(name);
+ return Status::ok();
+ }
+ Status getNumOpenSessions(int32_t* out) override {
+ *out = MyBinderRpcSession::gNum;
+ return Status::ok();
+ }
+
+ std::mutex blockMutex;
+ Status lock() override {
+ blockMutex.lock();
+ return Status::ok();
+ }
+ Status unlockInMsAsync(int32_t ms) override {
+ usleep(ms * 1000);
+ blockMutex.unlock();
+ return Status::ok();
+ }
+ Status lockUnlock() override {
+ std::lock_guard<std::mutex> _l(blockMutex);
+ return Status::ok();
+ }
+
+ Status sleepMs(int32_t ms) override {
+ usleep(ms * 1000);
+ return Status::ok();
+ }
+
+ Status sleepMsAsync(int32_t ms) override {
+ // In-process binder calls are asynchronous, but the call to this method
+ // is synchronous wrt its client. This in/out-process threading model
+ // diffentiation is a classic binder leaky abstraction (for better or
+ // worse) and is preserved here the way binder sockets plugs itself
+ // into BpBinder, as nothing is changed at the higher levels
+ // (IInterface) which result in this behavior.
+ return sleepMs(ms);
+ }
+
+ Status die(bool cleanup) override {
+ if (cleanup) {
+ exit(1);
+ } else {
+ _exit(1);
+ }
+ }
+};
+sp<IBinder> MyBinderRpcTest::mHeldBinder;
+
+class Process {
+public:
+ Process(const std::function<void()>& f) {
+ if (0 == (mPid = fork())) {
+ // racey: assume parent doesn't crash before this is set
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+ f();
+ }
+ }
+ ~Process() {
+ if (mPid != 0) {
+ kill(mPid, SIGKILL);
+ }
+ }
+
+private:
+ pid_t mPid = 0;
+};
+
+static std::string allocateSocketAddress() {
+ static size_t id = 0;
+
+ return "/dev/binderRpcTest_" + std::to_string(id++);
+};
+
+struct ProcessConnection {
+ // reference to process hosting a socket server
+ Process host;
+
+ // client connection object associated with other process
+ sp<RpcConnection> connection;
+
+ // pre-fetched root object
+ sp<IBinder> rootBinder;
+
+ // whether connection should be invalidated by end of run
+ bool expectInvalid = false;
+
+ ~ProcessConnection() {
+ rootBinder = nullptr;
+ EXPECT_NE(nullptr, connection);
+ EXPECT_NE(nullptr, connection->state());
+ EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:");
+
+ wp<RpcConnection> weakConnection = connection;
+ connection = nullptr;
+ EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+ }
+};
+
+// This creates a new process serving an interface on a certain number of
+// threads.
+ProcessConnection createRpcTestSocketServerProcess(
+ size_t numThreads,
+ const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
+ CHECK_GT(numThreads, 0);
+
+ std::string addr = allocateSocketAddress();
+ unlink(addr.c_str());
+
+ auto ret = ProcessConnection{
+ .host = Process([&] {
+ sp<RpcServer> server = RpcServer::make();
+
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+ // server supporting one client on one socket
+ sp<RpcConnection> connection = server->addClientConnection();
+ CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+
+ configure(server, connection);
+
+ // accept 'numThreads' connections
+ std::vector<std::thread> pool;
+ for (size_t i = 0; i + 1 < numThreads; i++) {
+ pool.push_back(std::thread([=] { connection->join(); }));
+ }
+ connection->join();
+ for (auto& t : pool) t.join();
+ }),
+ .connection = RpcConnection::make(),
+ };
+
+ // wait up to 1s for sockets to be created
+ constexpr useconds_t kMaxWaitUs = 1000000;
+ constexpr useconds_t kWaitDivision = 100;
+ for (size_t i = 0; i < kWaitDivision && 0 != access(addr.c_str(), F_OK); i++) {
+ usleep(kMaxWaitUs / kWaitDivision);
+ }
+
+ // create remainder of connections
+ for (size_t i = 0; i < numThreads; i++) {
+ // Connection refused sometimes after file created but before listening.
+ CHECK(ret.connection->addUnixDomainClient(addr.c_str()) ||
+ (usleep(10000), ret.connection->addUnixDomainClient(addr.c_str())))
+ << i;
+ }
+
+ ret.rootBinder = ret.connection->getRootObject();
+ return ret;
+}
+
+// Process connection where the process hosts IBinderRpcTest, the server used
+// for most testing here
+struct BinderRpcTestProcessConnection {
+ ProcessConnection proc;
+
+ // pre-fetched root object
+ sp<IBinder> rootBinder;
+
+ // pre-casted root object
+ sp<IBinderRpcTest> rootIface;
+
+ ~BinderRpcTestProcessConnection() {
+ if (!proc.expectInvalid) {
+ int32_t remoteBinders = 0;
+ EXPECT_OK(rootIface->countBinders(&remoteBinders));
+ // should only be the root binder object, iface
+ EXPECT_EQ(remoteBinders, 1);
+ }
+
+ rootIface = nullptr;
+ rootBinder = nullptr;
+ }
+};
+
+BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
+ BinderRpcTestProcessConnection ret{
+ .proc = createRpcTestSocketServerProcess(numThreads,
+ [&](const sp<RpcServer>& server,
+ const sp<RpcConnection>& connection) {
+ sp<MyBinderRpcTest> service =
+ new MyBinderRpcTest;
+ server->setRootObject(service);
+ service->connection =
+ connection; // for testing only
+ }),
+ };
+
+ ret.rootBinder = ret.proc.rootBinder;
+ ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+ return ret;
+}
+
+TEST(BinderRpc, RootObjectIsNull) {
+ auto proc = createRpcTestSocketServerProcess(1,
+ [](const sp<RpcServer>& server,
+ const sp<RpcConnection>&) {
+ // this is the default, but to be explicit
+ server->setRootObject(nullptr);
+ });
+
+ // retrieved by getRootObject when process is created above
+ EXPECT_EQ(nullptr, proc.rootBinder);
+
+ // make sure we can retrieve it again (process doesn't crash)
+ EXPECT_EQ(nullptr, proc.connection->getRootObject());
+}
+
+TEST(BinderRpc, Ping) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+TEST(BinderRpc, TransactionsMustBeMarkedRpc) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
+}
+
+TEST(BinderRpc, UnknownTransaction) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ Parcel data;
+ data.markForBinder(proc.rootBinder);
+ Parcel reply;
+ EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
+}
+
+TEST(BinderRpc, SendSomethingOneway) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ EXPECT_OK(proc.rootIface->sendString("asdf"));
+}
+
+TEST(BinderRpc, SendAndGetResultBack) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+TEST(BinderRpc, SendAndGetResultBackBig) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ std::string single = std::string(1024, 'a');
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
+ EXPECT_EQ(single + single, doubled);
+}
+
+TEST(BinderRpc, CallMeBack) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ int32_t pingResult;
+ EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
+ EXPECT_EQ(OK, pingResult);
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST(BinderRpc, RepeatBinder) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> inBinder = new MyBinderRpcSession("foo");
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST(BinderRpc, RepeatTheirBinder) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+
+ sp<IBinder> inBinder = IInterface::asBinder(session);
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ session = nullptr;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST(BinderRpc, RepeatBinderNull) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
+ EXPECT_EQ(nullptr, outBinder);
+}
+
+TEST(BinderRpc, HoldBinder) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ IBinder* ptr = nullptr;
+ {
+ sp<IBinder> binder = new BBinder();
+ ptr = binder.get();
+ EXPECT_OK(proc.rootIface->holdBinder(binder));
+ }
+
+ sp<IBinder> held;
+ EXPECT_OK(proc.rootIface->getHeldBinder(&held));
+
+ EXPECT_EQ(held.get(), ptr);
+
+ // stop holding binder, because we test to make sure references are cleaned
+ // up
+ EXPECT_OK(proc.rootIface->holdBinder(nullptr));
+ // and flush ref counts
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+// START TESTS FOR LIMITATIONS OF SOCKET BINDER
+// These are behavioral differences form regular binder, where certain usecases
+// aren't supported.
+
+TEST(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+ auto proc1 = createRpcTestSocketServerProcess(1);
+ auto proc2 = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
+}
+
+TEST(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
+}
+
+TEST(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ // for historical reasons, IServiceManager interface only returns the
+ // exception code
+ EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
+ defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
+}
+
+// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+
+TEST(BinderRpc, RepeatRootObject) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
+ EXPECT_EQ(proc.rootBinder, outBinder);
+}
+
+TEST(BinderRpc, NestedTransactions) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ auto nastyNester = sp<MyBinderRpcTest>::make();
+ EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
+
+ wp<IBinder> weak = nastyNester;
+ nastyNester = nullptr;
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST(BinderRpc, SameBinderEquality) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ EXPECT_EQ(a, b);
+}
+
+TEST(BinderRpc, SameBinderEqualityWeak) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ wp<IBinder> weak = a;
+ a = nullptr;
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ // this is the wrong behavior, since BpBinder
+ // doesn't implement onIncStrongAttempted
+ // but make sure there is no crash
+ EXPECT_EQ(nullptr, weak.promote());
+
+ GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
+
+ // In order to fix this:
+ // - need to have incStrongAttempted reflected across IPC boundary (wait for
+ // response to promote - round trip...)
+ // - sendOnLastWeakRef, to delete entries out of RpcState table
+ EXPECT_EQ(b, weak.promote());
+}
+
+#define expectSessions(expected, iface) \
+ do { \
+ int session; \
+ EXPECT_OK((iface)->getNumOpenSessions(&session)); \
+ EXPECT_EQ(expected, session); \
+ } while (false)
+
+TEST(BinderRpc, SingleSession) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+ std::string out;
+ EXPECT_OK(session->getName(&out));
+ EXPECT_EQ("aoeu", out);
+
+ expectSessions(1, proc.rootIface);
+ session = nullptr;
+ expectSessions(0, proc.rootIface);
+}
+
+TEST(BinderRpc, ManySessions) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ std::vector<sp<IBinderRpcSession>> sessions;
+
+ for (size_t i = 0; i < 15; i++) {
+ expectSessions(i, proc.rootIface);
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
+ sessions.push_back(session);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+ for (size_t i = 0; i < sessions.size(); i++) {
+ std::string out;
+ EXPECT_OK(sessions.at(i)->getName(&out));
+ EXPECT_EQ(std::to_string(i), out);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+
+ while (!sessions.empty()) {
+ sessions.pop_back();
+ expectSessions(sessions.size(), proc.rootIface);
+ }
+ expectSessions(0, proc.rootIface);
+}
+
+size_t epochMillis() {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::seconds;
+ using std::chrono::system_clock;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
+
+TEST(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+ constexpr size_t kNumThreads = 10;
+
+ auto proc = createRpcTestSocketServerProcess(kNumThreads);
+
+ EXPECT_OK(proc.rootIface->lock());
+
+ // block all but one thread taking locks
+ std::vector<std::thread> ts;
+ for (size_t i = 0; i < kNumThreads - 1; i++) {
+ ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); }));
+ }
+
+ usleep(100000); // give chance for calls on other threads
+
+ // other calls still work
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ constexpr size_t blockTimeMs = 500;
+ size_t epochMsBefore = epochMillis();
+ // after this, we should never see a response within this time
+ EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs));
+
+ // this call should be blocked for blockTimeMs
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ size_t epochMsAfter = epochMillis();
+ EXPECT_GE(epochMsAfter, epochMsBefore + blockTimeMs) << epochMsBefore;
+
+ for (auto& t : ts) t.join();
+}
+
+TEST(BinderRpc, ThreadPoolOverSaturated) {
+ constexpr size_t kNumThreads = 10;
+ constexpr size_t kNumCalls = kNumThreads + 3;
+ constexpr size_t kSleepMs = 500;
+
+ auto proc = createRpcTestSocketServerProcess(kNumThreads);
+
+ size_t epochMsBefore = epochMillis();
+
+ std::vector<std::thread> ts;
+ for (size_t i = 0; i < kNumCalls; i++) {
+ ts.push_back(std::thread([&] { proc.rootIface->sleepMs(kSleepMs); }));
+ }
+
+ for (auto& t : ts) t.join();
+
+ size_t epochMsAfter = epochMillis();
+
+ EXPECT_GE(epochMsAfter, epochMsBefore + 2 * kSleepMs);
+
+ // Potential flake, but make sure calls are handled in parallel.
+ EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
+}
+
+TEST(BinderRpc, ThreadingStressTest) {
+ constexpr size_t kNumClientThreads = 10;
+ constexpr size_t kNumServerThreads = 10;
+ constexpr size_t kNumCalls = 100;
+
+ auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
+
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < kNumClientThreads; i++) {
+ threads.push_back(std::thread([&] {
+ for (size_t j = 0; j < kNumCalls; j++) {
+ sp<IBinder> out;
+ proc.rootIface->repeatBinder(proc.rootBinder, &out);
+ EXPECT_EQ(proc.rootBinder, out);
+ }
+ }));
+ }
+
+ for (auto& t : threads) t.join();
+}
+
+TEST(BinderRpc, OnewayCallDoesNotWait) {
+ constexpr size_t kReallyLongTimeMs = 100;
+ constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
+
+ // more than one thread, just so this doesn't deadlock
+ auto proc = createRpcTestSocketServerProcess(2);
+
+ size_t epochMsBefore = epochMillis();
+
+ EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
+
+ size_t epochMsAfter = epochMillis();
+ EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
+}
+
+TEST(BinderRpc, OnewayCallQueueing) {
+ constexpr size_t kNumSleeps = 10;
+ constexpr size_t kNumExtraServerThreads = 4;
+ constexpr size_t kSleepMs = 50;
+
+ // make sure calls to the same object happen on the same thread
+ auto proc = createRpcTestSocketServerProcess(1 + kNumExtraServerThreads);
+
+ EXPECT_OK(proc.rootIface->lock());
+
+ for (size_t i = 0; i < kNumSleeps; i++) {
+ // these should be processed serially
+ proc.rootIface->sleepMsAsync(kSleepMs);
+ }
+ // should also be processesed serially
+ EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
+
+ size_t epochMsBefore = epochMillis();
+ EXPECT_OK(proc.rootIface->lockUnlock());
+ size_t epochMsAfter = epochMillis();
+
+ EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+}
+
+TEST(BinderRpc, Die) {
+ // TODO(b/183141167): handle this in library
+ signal(SIGPIPE, SIG_IGN);
+
+ for (bool doDeathCleanup : {true, false}) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ // make sure there is some state during crash
+ // 1. we hold their binder
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("happy", &session));
+ // 2. they hold our binder
+ sp<IBinder> binder = new BBinder();
+ EXPECT_OK(proc.rootIface->holdBinder(binder));
+
+ EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
+ << "Do death cleanup: " << doDeathCleanup;
+
+ proc.proc.expectInvalid = true;
+ }
+}
+
+ssize_t countFds() {
+ DIR* dir = opendir("/proc/self/fd/");
+ if (dir == nullptr) return -1;
+ ssize_t ret = 0;
+ dirent* ent;
+ while ((ent = readdir(dir)) != nullptr) ret++;
+ closedir(dir);
+ return ret;
+}
+
+TEST(BinderRpc, Fds) {
+ ssize_t beforeFds = countFds();
+ ASSERT_GE(beforeFds, 0);
+ {
+ auto proc = createRpcTestSocketServerProcess(10);
+ ASSERT_EQ(OK, proc.rootBinder->pingBinder());
+ }
+ ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
+}
+
+extern "C" int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+ return RUN_ALL_TESTS();
+}
+
+} // namespace android
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 1f2779a..4270540 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -131,6 +131,17 @@
EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf()));
}
+TEST(BinderStability, ForceDowngradeStability) {
+ sp<IBinder> someBinder = BadStableBinder::vintf();
+
+ EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder));
+
+ // silly to do this after already using the binder, but it's for the test
+ Stability::forceDowngradeCompilationUnit(someBinder);
+
+ EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
+}
+
TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
sp<IBinder> vintfServer = BadStableBinder::vintf();
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
index ce99f59..b37030e 100644
--- a/libs/binder/tests/binderTextOutputTest.cpp
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -26,7 +26,6 @@
#include <binder/Parcel.h>
#include <binder/TextOutput.h>
-#include <binder/Debug.h>
static void CheckMessage(CapturedStderr& cap,
const char* expected,
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
index c950020..5e3502a 100644
--- a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
+++ b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
@@ -16,7 +16,6 @@
#include <fuzzer/FuzzedDataProvider.h>
-#include <binder/Debug.h>
#include <binder/Parcel.h>
#include <binder/TextOutput.h>
#include "android-base/file.h"
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 5bccaca..97626be 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -1,3 +1,12 @@
+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"],
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index c0bb75f..8c28464 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,6 +1,4 @@
chrisforbes@google.com
cnorthrop@google.com
-courtneygo@google.com
lpy@google.com
timvp@google.com
-zzyiwei@google.com
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index dc91fc6..ff059d7 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -149,6 +149,10 @@
sampling: true,
profile_file: "libgui/libgui.profdata",
},
+
+ lto: {
+ thin: true,
+ },
}
// Used by media codec services exclusively as a static lib for
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 82c9268..1976b49 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -130,8 +130,7 @@
}
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
- int width, int height, int32_t format,
- bool enableTripleBuffering)
+ int width, int height, int32_t format)
: mName(name),
mSurfaceControl(surface),
mSize(width, height),
@@ -143,9 +142,8 @@
// explicitly so that dequeueBuffer will block
mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
- if (enableTripleBuffering) {
- mProducer->setMaxDequeuedBufferCount(2);
- }
+ // safe default, most producers are expected to override this
+ mProducer->setMaxDequeuedBufferCount(2);
mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_HW_TEXTURE,
@@ -169,8 +167,6 @@
mNumAcquired = 0;
mNumFrameAvailable = 0;
- mPendingReleaseItem.item = BufferItem();
- mPendingReleaseItem.releaseFence = nullptr;
}
BLASTBufferQueue::~BLASTBufferQueue() {
@@ -242,7 +238,6 @@
std::unique_lock _lock{mMutex};
ATRACE_CALL();
BQA_LOGV("transactionCallback");
- mInitialCallbackReceived = true;
if (!stats.empty()) {
mTransformHint = stats[0].transformHint;
@@ -255,38 +250,20 @@
stats[0].frameEventStats.compositorTiming,
stats[0].latchTime,
stats[0].frameEventStats.dequeueReadyTime);
- }
- if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
- if (!stats.empty()) {
- mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
- } else {
- BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
- mPendingReleaseItem.releaseFence = nullptr;
+ currFrameNumber = stats[0].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;
}
- mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
- mPendingReleaseItem.releaseFence
- ? mPendingReleaseItem.releaseFence
- : Fence::NO_FENCE);
- mNumAcquired--;
- mPendingReleaseItem.item = BufferItem();
- mPendingReleaseItem.releaseFence = nullptr;
}
- if (mSubmitted.empty()) {
- BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
- }
- mPendingReleaseItem.item = std::move(mSubmitted.front());
- mSubmitted.pop();
-
- processNextBufferLocked(false /* useNextTransaction */);
-
- currFrameNumber = mPendingReleaseItem.item.mFrameNumber;
- if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) {
- transactionCompleteCallback = std::move(mTransactionCompleteCallback);
- mTransactionCompleteFrameNumber = 0;
- }
-
- mCallbackCV.notify_all();
decStrong((void*)transactionCallbackThunk);
}
@@ -295,15 +272,46 @@
}
}
+// 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();
+}
+
void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
ATRACE_CALL();
- BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
-
// 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)) {
- BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
mCallbackCV.notify_all();
return;
}
@@ -353,7 +361,7 @@
}
mNumAcquired++;
- mSubmitted.push(bufferItem);
+ mSubmitted[buffer->getId()] = bufferItem;
bool needsDisconnect = false;
mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -369,7 +377,10 @@
mLastBufferScalingMode = bufferItem.mScalingMode;
mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
- t->setBuffer(mSurfaceControl, buffer);
+ 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);
@@ -388,7 +399,7 @@
t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
if (!mNextFrameTimelineInfoQueue.empty()) {
- t->setFrameTimelineInfo(mSurfaceControl, mNextFrameTimelineInfoQueue.front());
+ t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
mNextFrameTimelineInfoQueue.pop();
}
@@ -427,9 +438,12 @@
}
BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
- " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d",
+ " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
+ " graphicBufferId=%" PRIu64,
mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
- bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size()));
+ bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
+ static_cast<uint32_t>(mPendingTransactions.size()),
+ bufferItem.mGraphicBuffer->getId());
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -444,18 +458,17 @@
std::unique_lock _lock{mMutex};
const bool nextTransactionSet = mNextTransaction != nullptr;
- BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s",
- item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));
-
- if (nextTransactionSet || mFlushShadowQueue) {
+ if (nextTransactionSet) {
while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
BQA_LOGV("waiting in onFrameAvailable...");
mCallbackCV.wait(_lock);
}
}
- mFlushShadowQueue = false;
// add to shadow queue
mNumFrameAvailable++;
+
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
+ toString(nextTransactionSet));
processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
}
@@ -514,14 +527,12 @@
}
// Check if we have acquired the maximum number of buffers.
-// As a special case, we wait for the first callback before acquiring the second buffer so we
-// can ensure the first buffer is presented if multiple buffers are queued in succession.
// 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 || (!mInitialCallbackReceived && mNumAcquired == 1);
+ return mNumAcquired == maxAcquiredBuffers;
}
class BBQSurface : public Surface {
@@ -595,7 +606,9 @@
// Apply the transaction since we have already acquired the desired frame.
t->apply();
} else {
- mPendingTransactions.emplace_back(frameNumber, std::move(*t));
+ mPendingTransactions.emplace_back(frameNumber, *t);
+ // Clear the transaction so it can't be applied elsewhere.
+ t->clear();
}
}
diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp
index f400774..9231a57 100644
--- a/libs/gui/FrameTimelineInfo.cpp
+++ b/libs/gui/FrameTimelineInfo.cpp
@@ -21,6 +21,7 @@
#include <android/os/IInputConstants.h>
#include <gui/FrameTimelineInfo.h>
#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
#include <utils/Errors.h>
#include <cmath>
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 05e1935..97f8f47 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -28,6 +28,7 @@
#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>
@@ -476,6 +477,26 @@
return reply.readInt32();
}
+ virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes) {
+ Parcel data, reply;
+ 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));
+ }
+ SAFE_PARCEL(data.writeInt32Vector, hdrTypesVector);
+
+ status_t result = remote()->transact(BnSurfaceComposer::OVERRIDE_HDR_TYPES, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("overrideHdrTypes failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+
status_t enableVSyncInjections(bool enable) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -755,11 +776,10 @@
return error;
}
- virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
- const sp<gui::IFpsListener>& listener) {
+ virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
Parcel data, reply;
SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(data.writeStrongBinder, layerHandle);
+ SAFE_PARCEL(data.writeInt32, taskId);
SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
const status_t error =
remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
@@ -936,7 +956,8 @@
return NO_ERROR;
}
- status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override {
+ 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) {
@@ -948,7 +969,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;
@@ -1669,8 +1690,8 @@
}
case ADD_FPS_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> layerHandle;
- status_t result = data.readNullableStrongBinder(&layerHandle);
+ int32_t taskId;
+ status_t result = data.readInt32(&taskId);
if (result != NO_ERROR) {
ALOGE("addFpsListener: Failed to read layer handle");
return result;
@@ -1681,7 +1702,7 @@
ALOGE("addFpsListener: Failed to read listener");
return result;
}
- return addFpsListener(layerHandle, listener);
+ return addFpsListener(taskId, listener);
}
case REMOVE_FPS_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1833,8 +1854,8 @@
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;
@@ -1962,6 +1983,20 @@
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);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 0ded936..b42793b 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,9 +17,10 @@
#define LOG_TAG "ITransactionCompletedListener"
//#define LOG_NDEBUG 0
-#include <gui/LayerState.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
namespace android {
@@ -27,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
@@ -122,6 +124,7 @@
for (const auto& data : jankData) {
SAFE_PARCEL(output->writeParcelable, data);
}
+ SAFE_PARCEL(output->writeUint64, previousBufferId);
return NO_ERROR;
}
@@ -144,6 +147,7 @@
SAFE_PARCEL(input->readParcelable, &data);
jankData.push_back(data);
}
+ SAFE_PARCEL(input->readUint64, &previousBufferId);
return NO_ERROR;
}
@@ -245,6 +249,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,6 +273,8 @@
case Tag::ON_TRANSACTION_COMPLETED:
return callLocalAsync(data, reply,
&ITransactionCompletedListener::onTransactionCompleted);
+ case Tag::ON_RELEASE_BUFFER:
+ return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
}
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 288bf92..d653ae7 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -23,6 +23,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
#include <utils/Errors.h>
#include <cmath>
@@ -41,7 +42,6 @@
flags(0),
mask(0),
reserved(0),
- crop_legacy(Rect::INVALID_RECT),
cornerRadius(0.0f),
backgroundBlurRadius(0),
barrierFrameNumber(0),
@@ -63,8 +63,8 @@
shouldBeSeamless(true),
fixedTransformHint(ui::Transform::ROT_INVALID),
frameNumber(0),
- frameTimelineInfo(),
- autoRefresh(false) {
+ autoRefresh(false),
+ releaseBufferListener(nullptr) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -85,7 +85,7 @@
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
SAFE_PARCEL(matrix.write, output);
- SAFE_PARCEL(output.write, crop_legacy);
+ SAFE_PARCEL(output.write, crop);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
@@ -151,8 +151,8 @@
SAFE_PARCEL(output.writeBool, shouldBeSeamless);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeUint64, frameNumber);
- SAFE_PARCEL(frameTimelineInfo.write, output);
SAFE_PARCEL(output.writeBool, autoRefresh);
+ SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
SAFE_PARCEL(output.writeUint32, blurRegions.size());
for (auto region : blurRegions) {
@@ -191,7 +191,7 @@
SAFE_PARCEL(input.readUint32, &mask);
SAFE_PARCEL(matrix.read, input);
- SAFE_PARCEL(input.read, crop_legacy);
+ SAFE_PARCEL(input.read, crop);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
@@ -273,9 +273,14 @@
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readUint64, &frameNumber);
- SAFE_PARCEL(frameTimelineInfo.read, input);
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();
@@ -407,10 +412,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;
@@ -540,14 +541,17 @@
what |= eFrameNumberChanged;
frameNumber = other.frameNumber;
}
- if (other.what & eFrameTimelineInfoChanged) {
- what |= eFrameTimelineInfoChanged;
- frameTimelineInfo.merge(other.frameTimelineInfo);
- }
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;
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index f3849bc..e91f74f 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -16,6 +16,8 @@
#include <gui/ScreenCaptureResults.h>
+#include <private/gui/ParcelUtils.h>
+
namespace android::gui {
status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f5cde59..0b01084 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -39,6 +39,7 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ParcelUtils.h>
#include <ui/DisplayMode.h>
#include <ui/DynamicDisplayInfo.h>
@@ -195,6 +196,17 @@
}
}
+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);
@@ -275,6 +287,23 @@
.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,
@@ -297,6 +326,32 @@
}
}
+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);
@@ -419,9 +474,8 @@
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),
mIsAutoTimestamp(other.mIsAutoTimestamp),
@@ -450,9 +504,8 @@
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();
@@ -527,9 +580,8 @@
mForceSynchronous = forceSynchronous;
mTransactionNestCount = transactionNestCount;
mAnimation = animation;
- mEarlyWakeup = earlyWakeup;
- mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
- mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
+ mEarlyWakeupStart = earlyWakeupStart;
+ mEarlyWakeupEnd = earlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
mIsAutoTimestamp = isAutoTimestamp;
@@ -559,9 +611,8 @@
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);
@@ -639,9 +690,8 @@
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);
@@ -659,9 +709,8 @@
mForceSynchronous = 0;
mTransactionNestCount = 0;
mAnimation = false;
- mEarlyWakeup = false;
- mExplicitEarlyWakeupStart = false;
- mExplicitEarlyWakeupEnd = false;
+ mEarlyWakeupStart = false;
+ mEarlyWakeupEnd = false;
mDesiredPresentTime = 0;
mIsAutoTimestamp = true;
mFrameTimelineInfo.clear();
@@ -780,17 +829,14 @@
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;
}
sp<IBinder> applyToken = mApplyToken
@@ -841,16 +887,12 @@
mAnimation = true;
}
-void SurfaceComposerClient::Transaction::setEarlyWakeup() {
- mEarlyWakeup = true;
+void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
+ mEarlyWakeupStart = true;
}
-void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
- mExplicitEarlyWakeupStart = true;
-}
-
-void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
- mExplicitEarlyWakeupEnd = true;
+void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
+ mEarlyWakeupEnd = true;
}
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
@@ -1048,15 +1090,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;
@@ -1204,20 +1246,6 @@
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);
@@ -1233,17 +1261,20 @@
}
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);
@@ -1251,6 +1282,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);
@@ -1458,7 +1517,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;
@@ -1562,20 +1621,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
const FrameTimelineInfo& frameTimelineInfo) {
- mFrameTimelineInfo = frameTimelineInfo;
- return *this;
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
- const sp<SurfaceControl>& sc, const FrameTimelineInfo& frameTimelineInfo) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
-
- s->what |= layer_state_t::eFrameTimelineInfoChanged;
- s->frameTimelineInfo = frameTimelineInfo;
+ mFrameTimelineInfo.merge(frameTimelineInfo);
return *this;
}
@@ -1939,6 +1985,11 @@
return ComposerService::getComposerService()->getAnimationFrameStats(outStats);
}
+status_t SurfaceComposerClient::overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes) {
+ return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes);
+}
+
status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
@@ -1982,9 +2033,9 @@
return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
}
-status_t SurfaceComposerClient::addFpsListener(const sp<IBinder>& layerHandle,
+status_t SurfaceComposerClient::addFpsListener(int32_t taskId,
const sp<gui::IFpsListener>& listener) {
- return ComposerService::getComposerService()->addFpsListener(layerHandle, listener);
+ return ComposerService::getComposerService()->addFpsListener(taskId, listener);
}
status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
@@ -1998,7 +2049,7 @@
}
status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken,
- float brightness) {
+ const gui::DisplayBrightness& brightness) {
return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 1dcfe2e..7e2f8f9 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -39,6 +39,7 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
+#include <private/gui/ParcelUtils.h>
namespace android {
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/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd8e714..fbd16f4 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -74,7 +74,7 @@
{
public:
BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
- int height, int32_t format, bool enableTripleBuffering = true);
+ int height, int32_t format);
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
return mProducer;
@@ -89,13 +89,14 @@
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, uint32_t width, uint32_t height, int32_t format);
- void flushShadowQueue() { mFlushShadowQueue = true; }
+ void flushShadowQueue() {}
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -132,16 +133,10 @@
int32_t mNumFrameAvailable GUARDED_BY(mMutex);
int32_t mNumAcquired GUARDED_BY(mMutex);
- bool mInitialCallbackReceived GUARDED_BY(mMutex) = false;
- struct PendingReleaseItem {
- BufferItem item;
- sp<Fence> releaseFence;
- };
- std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
- // Keep a reference to the currently presented buffer so we can release it when the next buffer
- // is ready to be presented.
- PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
+ // 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);
@@ -157,9 +152,6 @@
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
- // If set to true, the next queue buffer will wait until the shadow queue has been processed by
- // the adapter.
- bool mFlushShadowQueue = false;
// 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.
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8b64bcf..9f9ca74 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/gui/DisplayBrightness.h>
#include <android/gui/IFpsListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
@@ -89,18 +90,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 {
@@ -269,6 +267,13 @@
*/
virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0;
+ /* Overrides the supported HDR modes for the given display device.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes) = 0;
+
virtual status_t enableVSyncInjections(bool enable) = 0;
virtual status_t injectVSync(nsecs_t when) = 0;
@@ -351,16 +356,15 @@
/* Registers a listener that streams fps updates from SurfaceFlinger.
*
- * The listener will stream fps updates for the layer tree rooted at layerHandle. Usually, this
- * should be tied to a task. Layers that are not descendants of that task are out of scope for
- * FPS computations.
+ * 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 ACCESS_SURFACE_FLINGER permission.
+ * Requires the READ_FRAME_BUFFER permission.
*/
- virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
- const sp<gui::IFpsListener>& listener) = 0;
+ virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) = 0;
/*
* Removes a listener that was streaming fps updates from SurfaceFlinger.
*/
@@ -416,15 +420,15 @@
* 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 boost to the composer. This function is asynchronous.
@@ -573,6 +577,7 @@
GET_DYNAMIC_DISPLAY_INFO,
ADD_FPS_LISTENER,
REMOVE_FPS_LISTENER,
+ OVERRIDE_HDR_TYPES,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index cb17cee..098760e 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -87,13 +87,14 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
uint32_t hint, FrameEventHistoryStats frameEventStats,
- std::vector<JankData> jankData)
+ std::vector<JankData> jankData, uint64_t previousBufferId)
: surfaceControl(sc),
acquireTime(time),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
eventStats(frameEventStats),
- jankData(std::move(jankData)) {}
+ jankData(std::move(jankData)),
+ previousBufferId(previousBufferId) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
@@ -101,6 +102,7 @@
uint32_t transformHint = 0;
FrameEventHistoryStats eventStats;
std::vector<JankData> jankData;
+ uint64_t previousBufferId;
};
class TransactionStats : public Parcelable {
@@ -139,6 +141,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> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index b273805..9274777 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -16,23 +16,7 @@
#ifndef ANDROID_SF_LAYER_STATE_H
#define ANDROID_SF_LAYER_STATE_H
-#define SAFE_PARCEL(FUNC, ...) \
- { \
- status_t error = FUNC(__VA_ARGS__); \
- if (error) { \
- ALOGE("ERROR(%d). Failed to call parcel %s(%s)", 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; \
- } \
- }
#include <stdint.h>
#include <sys/types.h>
@@ -98,9 +82,9 @@
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
- eCropChanged_legacy = 0x00000100,
+ /* was eCropChanged_legacy, now available 0x00000100, */
eDeferTransaction_legacy = 0x00000200,
- /* was ScalingModeChanged, now available 0x00000400, */
+ eReleaseBufferListenerChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
eReparentChildren = 0x00001000,
/* was eDetachChildren, now available 0x00002000, */
@@ -133,10 +117,9 @@
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
eFrameNumberChanged = 0x400'00000000,
- eFrameTimelineInfoChanged = 0x800'00000000,
- eBlurRegionsChanged = 0x1000'00000000,
- eAutoRefreshChanged = 0x2000'00000000,
- eStretchChanged = 0x4000'00000000,
+ eBlurRegionsChanged = 0x800'00000000,
+ eAutoRefreshChanged = 0x1000'00000000,
+ eStretchChanged = 0x2000'00000000,
};
layer_state_t();
@@ -167,7 +150,6 @@
uint32_t mask;
uint8_t reserved;
matrix22_t matrix;
- Rect crop_legacy;
float cornerRadius;
uint32_t backgroundBlurRadius;
sp<SurfaceControl> barrierSurfaceControl_legacy;
@@ -240,8 +222,6 @@
// graphics producer.
uint64_t frameNumber;
- 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.
@@ -249,6 +229,11 @@
// 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 {
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
index 0ccc6e8..99c35c1 100644
--- a/libs/gui/include/gui/ScreenCaptureResults.h
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -16,15 +16,6 @@
#pragma once
-#define SAFE_PARCEL(FUNC, ...) \
- { \
- status_t error = FUNC(__VA_ARGS__); \
- if (error) { \
- ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
- return error; \
- } \
- }
-
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <ui/Fence.h>
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ab0347c..c38375c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -80,6 +80,9 @@
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*/,
@@ -209,7 +212,8 @@
* 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);
/*
* Sends a power boost to the composer. This function is asynchronous.
@@ -349,9 +353,8 @@
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;
@@ -387,6 +390,8 @@
void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+ void setReleaseBufferCallback(layer_state_t* state, ReleaseBufferCallback callback);
+ void removeReleaseBufferCallback(layer_state_t* state);
public:
Transaction();
@@ -436,7 +441,7 @@
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);
@@ -469,9 +474,9 @@
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);
@@ -524,9 +529,6 @@
// to the transaction, and the input event id that identifies the input event that caused
// the current frame.
Transaction& setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo);
- // Variant that only applies to a specific SurfaceControl.
- Transaction& setFrameTimelineInfo(const sp<SurfaceControl>& sc,
- 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
@@ -561,9 +563,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;
@@ -571,6 +572,9 @@
static status_t clearAnimationFrameStats();
static status_t getAnimationFrameStats(FrameStats* outStats);
+ static status_t overrideHdrTypes(const sp<IBinder>& display,
+ const std::vector<ui::Hdr>& hdrTypes);
+
static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
const Rect& layerStackRect, const Rect& displayRect);
@@ -589,8 +593,7 @@
const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener);
static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
- static status_t addFpsListener(const sp<IBinder>& layerHandle,
- const sp<gui::IFpsListener>& listener);
+ static status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener);
static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
private:
@@ -651,6 +654,8 @@
std::unordered_map<CallbackId, CallbackTranslation> 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);
@@ -684,8 +689,15 @@
SurfaceStatsCallback listener);
void removeSurfaceStatsListener(void* context, void* cookie);
- // Overrides BnTransactionCompletedListener's onTransactionCompleted
+ 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/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/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fb07a19..fe48d88 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -73,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 {
@@ -128,7 +149,7 @@
mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
}
- void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+ void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) {
producer = adapter.getIGraphicBufferProducer();
setUpProducer(producer);
}
@@ -205,10 +226,10 @@
EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
}
+ ASSERT_EQ(false, ::testing::Test::HasFailure());
}
}
captureBuf->unlock();
- ASSERT_EQ(false, ::testing::Test::HasFailure());
}
static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
@@ -315,7 +336,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);
@@ -351,7 +373,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);
@@ -396,7 +419,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);
@@ -429,7 +453,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);
@@ -458,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();
@@ -486,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);
@@ -537,7 +563,8 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
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);
@@ -577,7 +604,7 @@
sp<IGraphicBufferProducer> slowIgbProducer;
setUpProducer(slowAdapter, slowIgbProducer);
nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
- queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);
+ queueBuffer(slowIgbProducer, 0 /* r */, 255 /* g */, 0 /* b */, presentTimeDelay);
BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> fastIgbProducer;
@@ -617,7 +644,8 @@
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_SCALE_TO_WINDOW,
tr, Fence::NO_FENCE);
@@ -838,6 +866,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);
@@ -848,7 +877,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;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index d65a40b..59e5c13 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -195,7 +195,7 @@
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
t.setLayer(mSurfaceControl, LAYER_BASE);
t.setPosition(mSurfaceControl, x, y);
- t.setCrop_legacy(mSurfaceControl, crop);
+ t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
t.apply(true);
}
@@ -670,7 +670,7 @@
parentSurface->showAt(100, 100);
nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
- t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
t.reparent(sc, parentSurface->mSurfaceControl);
});
@@ -694,7 +694,7 @@
parentSurface->showAt(50, 50);
nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
- t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
t.reparent(sc, parentSurface->mSurfaceControl);
});
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 91b2aff..5ac3f19 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -772,6 +772,10 @@
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
}
+ status_t overrideHdrTypes(const sp<IBinder>& /*display*/,
+ const std::vector<ui::Hdr>& /*hdrTypes*/) override {
+ return NO_ERROR;
+ }
status_t enableVSyncInjections(bool /*enable*/) override {
return NO_ERROR;
}
@@ -811,7 +815,7 @@
return NO_ERROR;
}
status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/,
- float /*brightness*/) override {
+ const gui::DisplayBrightness& /*brightness*/) override {
return NO_ERROR;
}
@@ -824,8 +828,7 @@
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
- status_t addFpsListener(const sp<IBinder>& /*layerHandle*/,
- const sp<gui::IFpsListener>& /*listener*/) {
+ status_t addFpsListener(int32_t /*taskId*/, const sp<gui::IFpsListener>& /*listener*/) {
return NO_ERROR;
}
status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 4253610..6f79f4b 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -83,13 +83,13 @@
"InputWindow.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/TouchOcclusionMode.aidl",
- "android/os/BlockUntrustedTouchesMode.aidl",
- "android/os/IInputFlinger.aidl",
"android/os/ISetInputWindowsListener.aidl",
+ "android/os/TouchOcclusionMode.aidl",
],
export_shared_lib_headers: ["libbinder"],
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 0a00d68..5600eb3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -92,6 +92,9 @@
case AINPUT_EVENT_TYPE_CAPTURE: {
return "CAPTURE";
}
+ case AINPUT_EVENT_TYPE_DRAG: {
+ return "DRAG";
+ }
}
return "UNKNOWN";
}
@@ -770,6 +773,23 @@
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) :
@@ -815,6 +835,15 @@
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:
@@ -842,6 +871,12 @@
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/InputTransport.cpp b/libs/input/InputTransport.cpp
index 6218fdc..c2a3cf1 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;
@@ -107,6 +108,8 @@
return true;
case Type::CAPTURE:
return true;
+ case Type::DRAG:
+ return true;
}
}
return false;
@@ -124,6 +127,8 @@
return sizeof(Header) + body.focus.size();
case Type::CAPTURE:
return sizeof(Header) + body.capture.size();
+ case Type::DRAG:
+ return sizeof(Header) + body.drag.size();
}
return sizeof(Header);
}
@@ -248,6 +253,13 @@
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;
+ }
}
}
@@ -576,8 +588,8 @@
msg.header.type = InputMessage::Type::FOCUS;
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);
}
@@ -594,28 +606,49 @@
msg.header.type = InputMessage::Type::CAPTURE;
msg.header.seq = seq;
msg.body.capture.eventId = eventId;
- msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled ? 1 : 0;
+ msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled;
return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::receiveFinishedSignal(
- const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) {
+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::Finished> InputPublisher::receiveFinishedSignal() {
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) {
- 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;
+ 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);
}
- callback(msg.header.seq, msg.body.finished.handled == 1, msg.body.finished.consumeTime);
- return OK;
+ return Finished{
+ .seq = msg.header.seq,
+ .handled = msg.body.finished.handled,
+ .consumeTime = msg.body.finished.consumeTime,
+ };
}
// --- InputConsumer ---
@@ -753,8 +786,9 @@
}
case InputMessage::Type::FINISHED: {
- LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
- "InputConsumer!");
+ LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
+ "InputConsumer!",
+ NamedEnum::string(mMsg.header.type).c_str());
break;
}
@@ -777,6 +811,16 @@
*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;
@@ -1166,7 +1210,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
msg.header.seq = seq;
- msg.body.finished.handled = handled ? 1 : 0;
+ msg.body.finished.handled = handled;
msg.body.finished.consumeTime = getConsumeTime(seq);
status_t result = mChannel->sendMessage(&msg);
if (result == OK) {
@@ -1226,12 +1270,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 == 1);
+ 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) {
@@ -1299,14 +1348,14 @@
out = out + "mChannel = " + mChannel->getName() + "\n";
out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
if (mMsgDeferred) {
- out = out + "mMsg : " + InputMessage::typeToString(mMsg.header.type) + "\n";
+ 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,
- InputMessage::typeToString(msg.header.type));
+ NamedEnum::string(msg.header.type).c_str());
switch (msg.header.type) {
case InputMessage::Type::KEY: {
out += android::base::StringPrintf("action=%s keycode=%" PRId32,
@@ -1344,6 +1393,12 @@
.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;
+ }
}
out += "\n";
}
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index bce0ec8..4b90844 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -20,7 +20,10 @@
/** @hide */
interface IInputConstants
{
- const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
+ // 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.
/**
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
index 0dbb4cf..6de030f 100644
--- a/libs/input/tests/Flags_test.cpp
+++ b/libs/input/tests/Flags_test.cpp
@@ -148,6 +148,11 @@
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");
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index e7e566d..fc31715 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -27,6 +27,8 @@
#include <utils/StopWatch.h>
#include <utils/Timers.h>
+using android::base::Result;
+
namespace android {
class InputPublisherAndConsumerTest : public testing::Test {
@@ -52,6 +54,7 @@
void PublishAndConsumeMotionEvent();
void PublishAndConsumeFocusEvent();
void PublishAndConsumeCaptureEvent();
+ void PublishAndConsumeDragEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -121,23 +124,13 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- 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";
- ASSERT_GE(consumeTime, publishTime)
+ Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -271,23 +264,13 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = true;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- 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";
- ASSERT_GE(consumeTime, publishTime)
+ Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_FALSE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -301,7 +284,7 @@
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;
@@ -321,22 +304,14 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- 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";
- ASSERT_GE(consumeTime, publishTime)
+ Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+
+ ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -349,7 +324,7 @@
const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
- ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+ ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK";
uint32_t consumeSeq;
InputEvent* event;
@@ -368,22 +343,55 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- uint32_t finishedSeq = 0;
- bool handled = false;
- nsecs_t consumeTime;
- status = mPublisher->receiveFinishedSignal(
- [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- finishedSeq = inSeq;
- handled = inHandled;
- consumeTime = inConsumeTime;
- });
- ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, finishedSeq)
+ android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
+ << "receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(result->handled)
+ << "receiveFinishedSignal should have set handled to consumer's reply";
+ ASSERT_GE(result->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";
+
+ android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, result->seq)
<< "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(handled)
+ ASSERT_TRUE(result->handled)
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(consumeTime, publishTime)
+ ASSERT_GE(result->consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -403,6 +411,10 @@
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;
@@ -468,6 +480,7 @@
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());
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index a886585..3d80b38 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -49,6 +49,7 @@
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
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);
@@ -60,6 +61,7 @@
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, dsdx, 96);
@@ -73,16 +75,26 @@
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, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
+ CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
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::Finished, handled, 4);
+ 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);
}
@@ -104,6 +116,7 @@
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);
}
// --- VerifiedInputEvent ---
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index a375d43..79b47a1 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -51,13 +51,13 @@
std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
status_t err = gbuffer->initCheck();
- if (err != 0 || gbuffer->handle == 0) {
+ if (err != 0 || gbuffer->handle == nullptr) {
if (err == NO_MEMORY) {
GraphicBuffer::dumpAllocationsToSystemLog();
}
ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p",
desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle);
- return err;
+ return err == 0 ? UNKNOWN_ERROR : err;
}
*outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb3b434..026b19a 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -84,6 +84,7 @@
name: "librenderengine_skia_sources",
srcs: [
"skia/AutoBackendTexture.cpp",
+ "skia/Cache.cpp",
"skia/ColorSpaces.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index b2ad22d..0c5a851 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -49,7 +49,8 @@
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
return renderengine::threaded::RenderEngineThreaded::create(
- [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+ [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
+ renderEngineType);
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
@@ -66,12 +67,14 @@
.setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly)
.setSupportsBackgroundBlur(args.supportsBackgroundBlur)
.setContextPriority(args.contextPriority)
- .setRenderEngineType(RenderEngineType::SKIA_GL_THREADED)
+ .setRenderEngineType(renderEngineType)
.build();
ALOGD("Threaded RenderEngine with SkiaGL Backend");
- return renderengine::threaded::RenderEngineThreaded::create([skiaArgs]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
- });
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [skiaArgs]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
+ },
+ renderEngineType);
}
case RenderEngineType::GLES:
default:
@@ -82,5 +85,15 @@
RenderEngine::~RenderEngine() = default;
+void RenderEngine::validateInputBufferUsage(const sp<GraphicBuffer>& buffer) {
+ LOG_ALWAYS_FATAL_IF(!(buffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE),
+ "input buffer not gpu readable");
+}
+
+void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) {
+ LOG_ALWAYS_FATAL_IF(!(buffer->getUsage() & GraphicBuffer::USAGE_HW_RENDER),
+ "output buffer not gpu writeable");
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 70ae0b2..a2963a7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -379,7 +379,8 @@
GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLConfig config, EGLContext ctxt, EGLSurface stub,
EGLContext protectedContext, EGLSurface protectedStub)
- : mEGLDisplay(display),
+ : RenderEngine(args.renderEngineType),
+ mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
mStubSurface(stub),
@@ -514,7 +515,7 @@
return mDrawingBuffer.get();
}
-void GLESRenderEngine::primeCache() const {
+void GLESRenderEngine::primeCache() {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
mUseColorManagement, mPrecacheToneMapperShaderOnly);
}
@@ -1124,6 +1125,8 @@
return BAD_VALUE;
}
+ validateOutputBufferUsage(buffer);
+
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.
@@ -1248,6 +1251,7 @@
isOpaque = layer->source.buffer.isOpaque;
sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+ validateInputBufferUsage(gBuf);
bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
layer->source.buffer.fence);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 7496b74..06a1722 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
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 cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 163a163..7c51f1b 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -87,13 +87,17 @@
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
+ RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+
+ RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
+
virtual ~RenderEngine() = 0;
// ----- BEGIN DEPRECATED INTERFACE -----
// 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;
@@ -192,8 +196,17 @@
// also supports background blur. If false, no blur will be applied when drawing layers.
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:
friend class threaded::RenderEngineThreaded;
+ const RenderEngineType mRenderEngineType;
};
struct RenderEngineCreationArgs {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 895ba3f..5f75b81 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_CONST_METHOD0(primeCache, void());
+ MOCK_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
new file mode 100644
index 0000000..69c6a09
--- /dev/null
+++ b/libs/renderengine/skia/Cache.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 {
+
+// Warming shader cache, not framebuffer cache.
+constexpr bool kUseFrameBufferCache = false;
+
+static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer) {
+ // 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,
+ },
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ // The identity matrix will generate the fast shaders, and the second matrix
+ // (based on one seen while going from dialer to the home screen) will
+ // generate the slower (more general case) version. If we also need a
+ // slow version without color correction, we should use this matrix with
+ // display.outputDataspace set to SRGB.
+ bool identity = true;
+ for (const mat4 transform : { mat4(), mat4(0.728872f, 0.f, 0.f, 0.f,
+ 0.f, 0.727627f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 167.355743f, 1852.257812f, 0.f, 1.f) }) {
+ layer.geometry.positionTransform = transform;
+ renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ identity = false;
+ }
+}
+
+static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
+ 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 = srcBuffer,
+ .maxMasteringLuminance = 1000.f,
+ .maxContentLuminance = 1000.f,
+ }},
+ };
+
+ // This matrix is based on actual data seen when opening the dialer.
+ // translate and scale creates new shaders when combined with rounded corners
+ // clang-format off
+ auto scale_and_translate = mat4(.19f, .0f, .0f, .0f,
+ .0f, .19f, .0f, .0f,
+ .0f, .0f, 1.f, .0f,
+ 169.f, 1527.f, .0f, 1.f);
+ // clang-format on
+
+ // Test both drawRect and drawRRect
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (auto transform : {mat4(), scale_and_translate}) {
+ layer.geometry.positionTransform = transform;
+ // fractional corner radius creates a shader that is used during home button swipe
+ for (float roundedCornersRadius : {0.0f, 0.05f, 500.f}) {
+ // roundedCornersCrop is always set, but it is this radius that triggers the behavior
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ // No need to check UNKNOWN, which is treated as SRGB.
+ for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
+ layer.sourceDataspace = dataspace;
+ 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, dstBuffer, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ },
+ .alpha = 1,
+ .source =
+ PixelSource{
+ .solidColor = half3(0.1f, 0.2f, 0.3f),
+ },
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(),
+ nullptr);
+}
+
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+ 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 = ui::Dataspace::DISPLAY_P3,
+ };
+
+ 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");
+ // 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");
+ drawSolidLayers(renderengine, display, dstBuffer);
+ drawShadowLayers(renderengine, display, srcBuffer);
+ drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
new file mode 100644
index 0000000..437571e
--- /dev/null
+++ b/libs/renderengine/skia/Cache.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+namespace android::renderengine::skia {
+
+class SkiaRenderEngine;
+
+class Cache {
+public:
+ static void primeShaderCache(SkiaRenderEngine*);
+
+private:
+ Cache() = default;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 9f40011..c5ee15d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -40,6 +40,7 @@
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
+#include "Cache.h"
#include <cmath>
#include <cstdint>
@@ -224,6 +225,10 @@
return engine;
}
+void SkiaGLRenderEngine::primeCache() {
+ Cache::primeShaderCache(this);
+}
+
EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
status_t err;
EGLConfig config;
@@ -263,22 +268,40 @@
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);
+}
+
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
- : mEGLDisplay(display),
+ : SkiaRenderEngine(args.renderEngineType),
+ mEGLDisplay(display),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
mProtectedPlaceholderSurface(protectedPlaceholder),
- mUseColorManagement(args.useColorManagement),
- mRenderEngineType(args.renderEngineType) {
+ mUseColorManagement(args.useColorManagement) {
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
options.fPreferExternalImagesOverES3 = true;
options.fDisableDistanceFieldPaths = true;
+ options.fPersistentCache = &mSkSLCacheMonitor;
mGrContext = GrDirectContext::MakeGL(glInterface, options);
if (useProtectedContext(true)) {
mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
@@ -293,11 +316,9 @@
}
SkiaGLRenderEngine::~SkiaGLRenderEngine() {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- mRuntimeEffects.clear();
- mProtectedTextureCache.clear();
- mTextureCache.clear();
+ cleanFramebufferCache();
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
if (mBlurFilter) {
delete mBlurFilter;
}
@@ -404,10 +425,6 @@
return true;
}
-static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) {
- return !!(desc.usage & usage);
-}
-
static float toDegrees(uint32_t transform) {
switch (transform) {
case ui::Transform::ROT_90:
@@ -462,18 +479,31 @@
}
ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- auto iter = mTextureCache.find(buffer->getId());
- if (iter != mTextureCache.end()) {
- ALOGV("Texture already exists in cache.");
+ // 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);
+ auto iter = cache.find(buffer->getId());
+ if (iter != cache.end()) {
+ ALOGV("Texture already exists in cache.");
} else {
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
std::make_shared<AutoBackendTexture::LocalRef>();
imageTextureRef->setTexture(
- new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), false));
- mTextureCache.insert({buffer->getId(), imageTextureRef});
+ new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), false));
+ cache.insert({buffer->getId(), imageTextureRef});
}
+ // restore the original state of the protected context if necessary
+ useProtectedContext(protectedContextState);
}
void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
@@ -602,12 +632,12 @@
return BAD_VALUE;
}
+ validateOutputBufferUsage(buffer);
+
auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
- LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
- "missing usage");
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
if (useFramebufferCache) {
@@ -650,9 +680,8 @@
if (mBlurFilter) {
bool requiresCompositionLayer = false;
for (const auto& layer : layers) {
- if (layer->backgroundBlurRadius > 0) {
- // when skbug.com/11208 and b/176903027 are resolved we can add the additional
- // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius
+ if (layer->backgroundBlurRadius > 0 &&
+ layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
requiresCompositionLayer = true;
}
for (auto region : layer->blurRegions) {
@@ -797,6 +826,8 @@
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;
}
@@ -806,7 +837,7 @@
needsToneMapping(layer->sourceDataspace, display.outputDataspace));
// quick abort from drawing the remaining portion of the layer
- if (layer->alpha == 0 && !requiresLinearEffect &&
+ if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
(!displayColorTransform || displayColorTransform->isAlphaUnchanged())) {
continue;
}
@@ -821,17 +852,18 @@
SkPaint paint;
if (layer->source.buffer.buffer) {
ATRACE_NAME("DrawImage");
+ validateInputBufferUsage(layer->source.buffer.buffer);
const auto& item = layer->source.buffer;
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
- auto iter = mTextureCache.find(item.buffer->getId());
- if (iter != mTextureCache.end()) {
+ auto iter = cache.find(item.buffer->getId());
+ if (iter != cache.end()) {
imageTextureRef = iter->second;
} else {
imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(),
item.buffer->toAHardwareBuffer(),
false));
- mTextureCache.insert({item.buffer->getId(), imageTextureRef});
+ cache.insert({item.buffer->getId(), imageTextureRef});
}
sk_sp<SkImage> image =
@@ -911,6 +943,10 @@
requiresLinearEffect));
}
+ if (layer->disableBlending) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+
paint.setColorFilter(displayColorTransform);
if (layer->geometry.roundedCornersRadius > 0) {
@@ -1136,7 +1172,14 @@
return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
}
-void SkiaGLRenderEngine::cleanFramebufferCache() {}
+void SkiaGLRenderEngine::cleanFramebufferCache() {
+ // TODO(b/180767535) Remove this method and use b/180767535 instead, which would allow
+ // SF to control texture lifecycle more tightly rather than through custom hooks into RE.
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mRuntimeEffects.clear();
+ mProtectedTextureCache.clear();
+ mTextureCache.clear();
+}
int SkiaGLRenderEngine::getContextPriority() {
int value;
@@ -1156,6 +1199,8 @@
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::lock_guard<std::mutex> lock(mRenderingMutex);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index ad26206..7605df9 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -31,6 +31,7 @@
#include "AutoBackendTexture.h"
#include "EGL/egl.h"
+#include "GrContextOptions.h"
#include "SkImageInfo.h"
#include "SkiaRenderEngine.h"
#include "android-base/macros.h"
@@ -50,6 +51,7 @@
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
+ void primeCache() override;
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
void unbindExternalTextureBuffer(uint64_t bufferId) override;
status_t drawLayers(const DisplaySettings& display,
@@ -62,6 +64,7 @@
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+ void assertShadersCompiled(int numShaders) override;
protected:
void dump(std::string& result) override;
@@ -131,9 +134,28 @@
// Object to capture commands send to Skia.
std::unique_ptr<SkiaCapture> mCapture;
- // Keep this information as a local variable to determine whether the access of the GL
- // operations is working on the same threads.
- const RenderEngineType mRenderEngineType = RenderEngineType::SKIA_GL;
+ // 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
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 12b8586..59d7e2f 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -36,9 +36,10 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
+ SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
~SkiaRenderEngine() override {}
- virtual void primeCache() const 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 void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
@@ -57,10 +58,11 @@
};
virtual bool cleanupPostRender(CleanupMode) override { return true; };
virtual int getContextPriority() override { return 0; }
+ virtual void assertShadersCompiled(int numShaders) {}
};
} // namespace skia
} // namespace renderengine
} // namespace android
-#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 5960e48..ec710d9 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -36,13 +36,18 @@
SkString blurString(R"(
in shader input;
uniform float2 in_blurOffset;
+ uniform float2 in_maxSizeXY;
half4 main(float2 xy) {
half4 c = sample(input, xy);
- c += sample(input, xy + float2( in_blurOffset.x, in_blurOffset.y));
- c += sample(input, xy + float2( in_blurOffset.x, -in_blurOffset.y));
- c += sample(input, xy + float2(-in_blurOffset.x, in_blurOffset.y));
- c += sample(input, xy + float2(-in_blurOffset.x, -in_blurOffset.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)));
+ 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);
}
@@ -99,6 +104,8 @@
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));
@@ -108,6 +115,8 @@
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);
}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 500a90b..7846156 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1920,6 +1920,83 @@
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.
+ auto buf = allocateSourceBuffer(1, 1);
+ {
+ uint8_t* pixels;
+ buf->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->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
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 08b672a..63aa4c8 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -32,7 +32,8 @@
void SetUp() override {
mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
- [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); },
+ renderengine::RenderEngine::RenderEngineType::THREADED);
}
std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index f448135..6a91c7c 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -34,11 +34,13 @@
namespace renderengine {
namespace threaded {
-std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
- return std::make_unique<RenderEngineThreaded>(std::move(factory));
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory,
+ RenderEngineType type) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory), type);
}
-RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type)
+ : RenderEngine(type) {
ATRACE_CALL();
std::lock_guard lockThread(mThreadMutex);
@@ -84,7 +86,7 @@
}
}
-void RenderEngineThreaded::primeCache() const {
+void RenderEngineThreaded::primeCache() {
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 8279cbc..7694328 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -37,11 +37,12 @@
*/
class RenderEngineThreaded : public RenderEngine {
public:
- static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory,
+ RenderEngineType type);
- RenderEngineThreaded(CreateInstanceFactory factory);
+ RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
- void primeCache() const override;
+ void primeCache() override;
void dump(std::string& result) override;
@@ -72,7 +73,7 @@
/* ------------------------------------------------------------------------
* Threading
*/
- const char* const mThreadName = "RenderEngineThread";
+ const char* const mThreadName = "RenderEngine";
// Protects the creation and destruction of mThread.
mutable std::mutex mThreadMutex;
std::thread mThread GUARDED_BY(mThreadMutex);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index de2b9a4..74d17ce 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -94,6 +94,7 @@
"libarect",
"libmath",
],
+
}
cc_library_shared {
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/OWNERS b/opengl/OWNERS
index b505712..a9bd4bb 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,7 +1,6 @@
chrisforbes@google.com
cnorthrop@google.com
-courtneygo@google.com
ianelliott@google.com
jessehall@google.com
lpy@google.com
-zzyiwei@google.com
+timvp@google.com
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index dc8e587..d38f2ef 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -22,6 +22,8 @@
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <EGL/Platform.h>
#pragma GCC diagnostic pop
+
+#include <android-base/properties.h>
#include <android/dlext.h>
#include <dlfcn.h>
#include <graphicsenv/GraphicsEnv.h>
@@ -33,7 +35,6 @@
namespace angle {
-constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
@@ -107,18 +108,36 @@
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
void* so = nullptr;
if (ns) {
+ // Loading from an APK, so hard-code the suffix to "_angle".
+ constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = ns,
};
so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
+ if (so) {
+ ALOGD("dlopen_ext from APK (%s) success at %p", kAngleEs2Lib, so);
+ } else {
+ ALOGE("dlopen_ext(\"%s\") failed: %s", kAngleEs2Lib, dlerror());
+ return false;
+ }
} else {
// If we are here, ANGLE is loaded as built-in gl driver in the sphal.
- so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
- }
- if (!so) {
- ALOGE("%s failed to dlopen %s!", __FUNCTION__, kAngleEs2Lib);
- return false;
+ // Get the specified ANGLE library filename suffix.
+ std::string angleEs2LibSuffix = android::base::GetProperty("ro.hardware.egl", "");
+ if (angleEs2LibSuffix.empty()) {
+ ALOGE("%s failed to get valid ANGLE library filename suffix!", __FUNCTION__);
+ return false;
+ }
+
+ std::string angleEs2LibName = "libGLESv2_" + angleEs2LibSuffix + ".so";
+ so = android_load_sphal_library(angleEs2LibName.c_str(), kAngleDlFlags);
+ if (so) {
+ ALOGD("dlopen (%s) success at %p", angleEs2LibName.c_str(), so);
+ } else {
+ ALOGE("%s failed to dlopen %s!", __FUNCTION__, angleEs2LibName.c_str());
+ return false;
+ }
}
angleGetDisplayPlatform =
@@ -129,8 +148,8 @@
return false;
}
- angleResetDisplayPlatform = reinterpret_cast<ResetDisplayPlatformFunc>(
- eglGetProcAddress("ANGLEResetDisplayPlatform"));
+ angleResetDisplayPlatform =
+ reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform"));
PlatformMethods* platformMethods = nullptr;
if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
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/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/gpuservice/OWNERS b/services/gpuservice/OWNERS
index 5d02839..ac300d0 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -1,3 +1,2 @@
chrisforbes@google.com
lpy@google.com
-zzyiwei@google.com
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 182736f..7bd0c6b 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -105,7 +105,7 @@
void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
- void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
@@ -113,6 +113,8 @@
void setPointerCapture(bool enabled) override {}
+ void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
+
InputDispatcherConfiguration mConfig;
};
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 393f649..9750ef9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -42,6 +42,7 @@
"InputTarget.cpp",
"Monitor.cpp",
"TouchState.cpp",
+ "DragState.cpp",
],
}
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 a19b04f..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -130,6 +130,23 @@
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 ---
KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 45a007b..f3ef64b 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -38,6 +38,7 @@
MOTION,
SENSOR,
POINTER_CAPTURE_CHANGED,
+ DRAG,
};
int32_t id;
@@ -111,6 +112,18 @@
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;
@@ -273,6 +286,9 @@
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/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index ee6b38b..2db8c13 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -40,110 +40,102 @@
return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
}
-std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
- auto it = mPendingFocusRequests.find(displayId);
- return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
+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) {
- // If the current focused window becomes unfocusable, remove focus.
- sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ std::string removeFocusReason;
+
+ // Check if the currently focused window is still focusable.
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
if (currentFocus) {
- FocusResult result = isTokenFocusable(currentFocus, windows);
- if (result != FocusResult::OK) {
- return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
+ 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);
}
}
- // Check if any pending focus requests can be resolved.
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (!pendingRequest) {
- return std::nullopt;
- }
-
- sp<IBinder> requestedFocus = pendingRequest->token;
- std::string windowName = pendingRequest->windowName;
- if (currentFocus == requestedFocus) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
- windowName.c_str(), displayId);
- mPendingFocusRequests.erase(displayId);
- return std::nullopt;
- }
-
- FocusResult result = isTokenFocusable(requestedFocus, windows);
- // If the window from the pending request is now visible, provide it focus.
- if (result == FocusResult::OK) {
- mPendingFocusRequests.erase(displayId);
- return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
- }
-
- if (result != FocusResult::NOT_VISIBLE) {
- // Drop the request if we are unable to change the focus for a reason other than visibility.
- ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
- displayId, NamedEnum::string(result).c_str());
- mPendingFocusRequests.erase(displayId);
- }
- return std::nullopt;
+ // 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 (request.focusedToken && 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;
- }
-
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (pendingRequest) {
- ALOGW("Pending focus request %s on display %" PRId32
- " ignored, reason:replaced by new request",
- pendingRequest->windowName.c_str(), displayId);
-
- // clear any pending focus requests
- mPendingFocusRequests.erase(displayId);
- }
-
if (currentFocus == request.token) {
ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
request.windowName.c_str(), displayId);
return std::nullopt;
}
- FocusResult result = isTokenFocusable(request.token, windows);
- if (result == FocusResult::OK) {
- std::string reason =
- (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
- return updateFocusedWindow(displayId, reason, request.token, request.windowName);
- }
-
- if (result == FocusResult::NOT_VISIBLE) {
- // The requested window is not currently visible. Wait for the window to become visible
- // and then provide it focus. This is to handle situations where a user action triggers
- // a new window to appear. We want to be able to queue any key events after the user
- // action and deliver it to the newly focused window. In order for this to happen, we
- // take focus from the currently focused window so key events can be queued.
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32
- " pending, reason: window is not visible",
- request.windowName.c_str(), displayId);
- mPendingFocusRequests[displayId] = request;
- return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
- } else {
- ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
+ // 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;
}
- 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::FocusResult FocusResolver::isTokenFocusable(
+FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
bool allWindowsAreFocusable = true;
bool visibleWindowFound = false;
@@ -165,16 +157,16 @@
}
if (!windowFound) {
- return FocusResult::NO_WINDOW;
+ return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
- return FocusResult::NOT_FOCUSABLE;
+ return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowFound) {
- return FocusResult::NOT_VISIBLE;
+ return Focusability::NOT_VISIBLE;
}
- return FocusResult::OK;
+ return Focusability::OK;
}
std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
@@ -209,15 +201,17 @@
std::string FocusResolver::dump() const {
std::string dump = dumpFocusedWindows();
-
- if (mPendingFocusRequests.empty()) {
- return dump + INDENT "PendingFocusRequests: <none>\n";
+ if (mFocusRequestByDisplay.empty()) {
+ return dump + INDENT "FocusRequests: <none>\n";
}
- dump += INDENT "PendingFocusRequests:\n";
- for (const auto& [displayId, request] : mPendingFocusRequests) {
- dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- request.windowName.c_str());
+ 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;
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index e067ad9..dc5eeeb 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -34,11 +34,18 @@
// 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 the window is not
-// visible, then the request is kept in a pending state and granted when it becomes visible.
-// If window becomes not focusable, or another request comes in, the pending request is dropped.
+// 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.
@@ -61,7 +68,7 @@
std::string dump() const;
private:
- enum class FocusResult {
+ enum class Focusability {
OK,
NO_WINDOW,
NOT_FOCUSABLE,
@@ -77,8 +84,8 @@
// 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 FocusResult isTokenFocusable(const sp<IBinder>& token,
- const std::vector<sp<InputWindowHandle>>& windows);
+ 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
@@ -87,15 +94,18 @@
typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
- // This map will store a single pending focus request per display that cannot be currently
- // processed. This can happen if the window requested to be focused is not currently visible.
- // Such a window might become visible later, and these requests would be processed at that time.
- std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
+ // 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> getPendingRequest(int32_t displayId);
+ std::optional<FocusRequest> getFocusRequest(int32_t displayId);
};
} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9898e9d..6977396 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -48,6 +48,7 @@
#define DEBUG_HOVER 0
#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>
@@ -78,6 +79,8 @@
#define INDENT3 " "
#define INDENT4 " "
+using android::base::HwTimeoutMultiplier;
+using android::base::Result;
using android::base::StringPrintf;
using android::os::BlockUntrustedTouchesMode;
using android::os::IInputConstants;
@@ -89,8 +92,9 @@
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
-constexpr std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT =
- std::chrono::milliseconds(android::os::IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
+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
@@ -329,10 +333,11 @@
int32_t inputTargetFlags) {
if (eventEntry->type == EventEntry::Type::MOTION) {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if (motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+ if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) == 0) {
const ui::Transform identityTransform;
- // Use identity transform for joystick events events because they don't depend on
- // the window info
+ // 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*/);
}
@@ -748,6 +753,14 @@
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: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
@@ -916,7 +929,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
// nothing to do
break;
}
@@ -938,7 +952,8 @@
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");
@@ -946,6 +961,9 @@
// Traverse windows from front to back to find touched window.
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) {
auto flags = windowInfo->flags;
@@ -1055,7 +1073,8 @@
case EventEntry::Type::SENSOR: {
break;
}
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::FOCUS:
@@ -1439,7 +1458,7 @@
ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
"source=0x%x, sensorType=%s",
entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
- NamedEnum::string(sensorType).c_str());
+ NamedEnum::string(entry->sensorType).c_str());
#endif
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
@@ -1549,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
@@ -1655,7 +1703,8 @@
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
- case EventEntry::Type::SENSOR: {
+ 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;
}
@@ -2012,6 +2061,8 @@
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()) {
@@ -2267,6 +2318,64 @@
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);
+ }
+ 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) {
+ mDragState.reset();
+ }
+}
+
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
@@ -2538,7 +2647,8 @@
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
if (eventEntry.type == EventEntry::Type::FOCUS ||
- eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+ 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;
@@ -2580,7 +2690,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s events are not user activity",
NamedEnum::string(eventEntry.type).c_str());
break;
@@ -2591,6 +2702,7 @@
std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventEntry.eventTime;
commandEntry->userActivityEventType = eventType;
+ commandEntry->displayId = displayId;
postCommandLocked(std::move(commandEntry));
}
@@ -2793,7 +2905,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::SENSOR: {
@@ -3016,6 +3129,15 @@
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::SENSOR: {
@@ -3188,17 +3310,17 @@
nsecs_t currentTime = now();
bool gotOne = false;
- status_t status;
+ status_t status = OK;
for (;;) {
- std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
- std::bind(&InputDispatcher::finishDispatchCycleLocked, d, currentTime,
- connection, std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3);
-
- status = connection->inputPublisher.receiveFinishedSignal(callback);
- if (status) {
+ Result<InputPublisher::Finished> result =
+ connection->inputPublisher.receiveFinishedSignal();
+ if (!result.ok()) {
+ status = result.error().code();
break;
}
+ const InputPublisher::Finished& finished = *result;
+ d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
+ finished.handled, finished.consumeTime);
gotOne = true;
}
if (gotOne) {
@@ -3313,7 +3435,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ 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;
@@ -3378,7 +3501,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
NamedEnum::string(downEventEntry->type).c_str());
break;
@@ -4342,6 +4466,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.
@@ -4532,7 +4664,8 @@
mBlockUntrustedTouchesMode = mode;
}
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+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.");
@@ -4576,6 +4709,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;
}
@@ -4716,6 +4854,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;
@@ -4886,8 +5029,7 @@
}
}
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
- const std::string& name) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATION
ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
@@ -4916,8 +5058,10 @@
return clientChannel;
}
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
- int32_t displayId, bool isGestureMonitor, const std::string& name, int32_t pid) {
+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);
@@ -5188,6 +5332,15 @@
postCommandLocked(std::move(commandEntry));
}
+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");
@@ -5297,6 +5450,13 @@
mLock.lock();
}
+void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
+ sp<IBinder> newToken = commandEntry->newToken;
+ mLock.unlock();
+ mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
+ mLock.lock();
+}
+
void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
@@ -5715,7 +5875,8 @@
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
+ commandEntry->displayId);
mLock.lock();
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 83094c2..5708fac 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -19,6 +19,7 @@
#include "AnrTracker.h"
#include "CancelationOptions.h"
+#include "DragState.h"
#include "Entry.h"
#include "FocusResolver.h"
#include "InjectionState.h"
@@ -120,8 +121,8 @@
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 base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
@@ -185,6 +186,9 @@
// Enqueues a focus event.
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(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -204,7 +208,8 @@
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);
@@ -336,6 +341,7 @@
REQUIRES(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, std::shared_ptr<InputApplicationHandle>>
@@ -387,6 +393,7 @@
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);
@@ -489,10 +496,13 @@
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;
@@ -585,6 +595,7 @@
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);
@@ -600,6 +611,8 @@
REQUIRES(mLock);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyFocusChangedLockedInterruptible(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);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3491893..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -149,8 +149,8 @@
*
* 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.
*/
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 909ce54..219f45a 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -134,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.
@@ -157,6 +157,9 @@
* 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
index ce64fe9..5b931d6 100644
--- a/services/inputflinger/docs/anr.md
+++ b/services/inputflinger/docs/anr.md
@@ -22,7 +22,7 @@
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 `IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS`.
+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.
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 18d0226..743587c 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -67,6 +67,7 @@
cflags: ["-Wall", "-Werror"],
shared_libs: [
+ "libbase",
"libbinder",
"libinputflingerhost",
"libutils",
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 47773d9..8112038 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -43,19 +43,19 @@
InputFlinger() ANDROID_API;
- virtual status_t dump(int fd, const Vector<String16>& args);
+ status_t dump(int fd, const Vector<String16>& args) override;
binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
- const sp<ISetInputWindowsListener>&) {
+ const sp<ISetInputWindowsListener>&) override {
return binder::Status::ok();
}
- binder::Status createInputChannel(const std::string&, InputChannel*) {
+ binder::Status createInputChannel(const std::string&, InputChannel*) override {
return binder::Status::ok();
}
- binder::Status removeInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
- binder::Status setFocusedWindow(const FocusRequest&) { 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/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 8a2828c..1af70a4 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -68,6 +68,7 @@
"libcutils",
"libinput",
"liblog",
+ "libstatslog",
"libui",
"libutils",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a0d3b6a..07011f5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -38,11 +38,13 @@
// #define LOG_NDEBUG 0
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/properties.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>
@@ -1361,13 +1363,14 @@
// Some devices report battery capacity as an integer through the "capacity" file
if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
- return std::stoi(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(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
- const auto it = BATTERY_LEVEL.find(buffer);
+ // Remove any white space such as trailing new line
+ const auto it = BATTERY_LEVEL.find(base::Trim(buffer));
if (it != BATTERY_LEVEL.end()) {
return it->second;
}
@@ -1389,9 +1392,8 @@
return std::nullopt;
}
- // Remove trailing new line
- buffer.erase(std::remove(buffer.begin(), buffer.end(), '\n'), buffer.end());
- const auto it = BATTERY_STATUS.find(buffer);
+ // Remove white space like trailing new line
+ const auto it = BATTERY_STATUS.find(base::Trim(buffer));
if (it != BATTERY_STATUS.end()) {
return it->second;
@@ -1460,7 +1462,7 @@
for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
it++) {
std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
- if (tryAddVideoDevice(*device, videoDevice)) {
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
// videoDevice was transferred to 'device'
it = mUnattachedVideoDevices.erase(it);
break;
@@ -1770,7 +1772,24 @@
}
}
-status_t EventHub::openDeviceLocked(const std::string& 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.c_str());
@@ -1778,7 +1797,7 @@
int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
- return -1;
+ return;
}
InputDeviceIdentifier identifier;
@@ -1797,7 +1816,7 @@
if (identifier.name == item) {
ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
- return -1;
+ return;
}
}
@@ -1806,7 +1825,7 @@
if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
- return -1;
+ return;
}
// Get device identifier.
@@ -1814,7 +1833,7 @@
if (ioctl(fd, EVIOCGID, &inputId)) {
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;
@@ -2012,7 +2031,7 @@
if (device->classes == Flags<InputDeviceClass>(0)) {
ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
device->identifier.name.c_str());
- return -1;
+ return;
}
// Classify InputDeviceClass::BATTERY.
@@ -2042,7 +2061,7 @@
}
if (registerDeviceForEpollLocked(*device) != OK) {
- return -1;
+ return;
}
device->configureFd();
@@ -2055,7 +2074,6 @@
toString(mBuiltInKeyboardId == deviceId));
addDeviceLocked(std::move(device));
- return OK;
}
void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
@@ -2066,7 +2084,7 @@
}
// Transfer ownership of this video device to a matching input device
for (const auto& [id, device] : mDevices) {
- if (tryAddVideoDevice(*device, videoDevice)) {
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
return; // 'device' now owns 'videoDevice'
}
}
@@ -2078,8 +2096,8 @@
mUnattachedVideoDevices.push_back(std::move(videoDevice));
}
-bool EventHub::tryAddVideoDevice(EventHub::Device& device,
- std::unique_ptr<TouchVideoDevice>& videoDevice) {
+bool EventHub::tryAddVideoDeviceLocked(EventHub::Device& device,
+ std::unique_ptr<TouchVideoDevice>& videoDevice) {
if (videoDevice->getName() != device.identifier.name) {
return false;
}
@@ -2153,6 +2171,7 @@
}
void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+ reportDeviceAddedForStatisticsLocked(device->identifier, device->classes);
mOpeningDevices.push_back(std::move(device));
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 4059729..2afaa85 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -567,8 +567,11 @@
bool configureLightsLocked();
};
- status_t openDeviceLocked(const std::string& devicePath);
- void openVideoDeviceLocked(const std::string& devicePath);
+ /**
+ * 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
@@ -576,39 +579,42 @@
* Return true if the association succeeds.
* Return false otherwise.
*/
- bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
- void createVirtualKeyboardLocked();
- void addDeviceLocked(std::unique_ptr<Device> device);
- void assignDescriptorLocked(InputDeviceIdentifier& identifier);
+ 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 std::string& 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);
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 std::string& 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 std::string& 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);
- int32_t getNextControllerNumberLocked(const std::string& name);
- void releaseControllerNumberLocked(int32_t num);
+ 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);
// Protect all internal state.
mutable std::mutex mLock;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index bb12be7..d6bd823 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -188,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::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;
+ }
}
}
@@ -294,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;
@@ -326,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) {
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 5344227..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) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 16cf010..13712ee 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -28,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
@@ -729,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;
@@ -1412,28 +1448,29 @@
}
void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
- const RawState* last =
- mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
-
// Push a new state.
mRawStatesPending.emplace_back();
- RawState* next = &mRawStatesPending.back();
- next->clear();
- next->when = when;
- next->readTime = readTime;
+ 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) {
@@ -1443,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*/);
@@ -1636,10 +1673,9 @@
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits,
- mViewport.displayId);
+ setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
}
bool TouchInputMapper::isTouchScreen() {
@@ -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);
@@ -2524,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();
@@ -2782,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();
@@ -2814,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
@@ -2883,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;
@@ -2918,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();
}
@@ -2940,8 +2971,7 @@
down = false;
}
- float x, y;
- mPointerController->getPosition(&x, &y);
+ auto [x, y] = getMouseCursorPosition();
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3014,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.
@@ -3353,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);
@@ -3401,7 +3431,7 @@
rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- mPointerController->move(deltaX, deltaY);
+ moveMouseCursor(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -3409,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);
@@ -3450,9 +3479,7 @@
}
int32_t displayId = mPointerController->getDisplayId();
- float xCursorPosition;
- float yCursorPosition;
- mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+ auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
@@ -3618,7 +3645,9 @@
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();
@@ -3731,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.
@@ -3746,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;
}
@@ -3777,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;
@@ -3883,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
@@ -3905,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);
@@ -3968,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 cb52e2d..5146299 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -772,10 +772,18 @@
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/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ef3dd65..17efb5b 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -18,6 +18,12 @@
#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 {
@@ -56,8 +62,7 @@
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
ASSERT_EQ(request.displayId, changes->displayId);
// invisible window cannot get focused
@@ -65,6 +70,7 @@
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;
@@ -99,19 +105,17 @@
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ 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_EQ(focusableWindowToken, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ 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_FALSE(changes);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, SetInputWindows) {
@@ -130,11 +134,10 @@
focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->newFocus);
- // Window visibility changes and the window loses focused
+ // Window visibility changes and the window loses focus
window->setVisible(false);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->newFocus);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, FocusRequestsCanBePending) {
@@ -158,8 +161,100 @@
// Window visibility changes and the window gets focused
invisibleWindow->setVisible(true);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ 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/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7ad5152..485fc99 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -266,6 +266,11 @@
mPointerCaptureEnabled.reset();
}
+ void assertDropTargetEquals(const sp<IBinder>& targetToken) {
+ std::scoped_lock lock(mLock);
+ ASSERT_EQ(targetToken, mDropTargetWindowToken);
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -284,6 +289,8 @@
std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
+ sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
+
void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
@@ -377,7 +384,7 @@
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
@@ -392,6 +399,11 @@
mPointerCaptureChangedCondition.notify_all();
}
+ void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
+ std::scoped_lock lock(mLock);
+ mDropTargetWindowToken = token;
+ }
+
void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
int32_t displayId) {
std::scoped_lock lock(mLock);
@@ -766,6 +778,9 @@
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;
}
@@ -803,6 +818,23 @@
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) {
@@ -898,16 +930,20 @@
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.transform.set(-frame.left, -frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
+ void addFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags |= flags; }
+
void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; }
@@ -999,6 +1035,10 @@
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";
@@ -1125,8 +1165,8 @@
return *this;
}
- MotionEventBuilder& buttonState(int32_t actionButton) {
- mActionButton = actionButton;
+ MotionEventBuilder& buttonState(int32_t buttonState) {
+ mButtonState = buttonState;
return *this;
}
@@ -2179,7 +2219,7 @@
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickNotTransformed) {
+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);
@@ -2199,28 +2239,41 @@
// Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
window->consumeFocusEvent(true);
- NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
- AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_DEFAULT);
- mDispatcher->notifyMotion(&motionArgs);
+ 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);
- // Third, we consume motion event.
- 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";
+ 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(AINPUT_EVENT_TYPE_MOTION, motionEvent.getAction());
+ 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();
+ float expectedX = motionArgs.pointerCoords[0].getX();
+ float expectedY = motionArgs.pointerCoords[0].getY();
- // Finally we test if 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 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";
+ }
}
/**
@@ -4264,6 +4317,19 @@
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;
@@ -4318,6 +4384,28 @@
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}}});
@@ -4358,9 +4446,52 @@
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, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4381,7 +4512,8 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) {
const sp<FakeWindowHandle>& w =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4392,9 +4524,11 @@
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, 0.7f);
+ getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& w2 =
- getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
touch();
@@ -4405,9 +4539,11 @@
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, 0.5f);
+ getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_FAR_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& w2 =
- getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY, 0.5f);
+ getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_FAR_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
touch();
@@ -4418,9 +4554,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest,
WindowsFromDifferentAppsEachBelowThreshold_AllowsTouch) {
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& wC =
- getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
touch();
@@ -4430,9 +4568,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowsFromDifferentAppsOneAboveThreshold_BlocksTouch) {
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& wC =
- getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
touch();
@@ -4443,9 +4583,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest,
WindowWithOpacityAboveThresholdAndSelfWindow_BlocksTouch) {
const sp<FakeWindowHandle>& wA =
- getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
touch();
@@ -4456,9 +4598,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest,
WindowWithOpacityBelowThresholdAndSelfWindow_AllowsTouch) {
const sp<FakeWindowHandle>& wA =
- getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
touch();
@@ -4468,7 +4612,8 @@
TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithOpacityAboveThreshold_AllowsTouch) {
const sp<FakeWindowHandle>& w =
- getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4513,7 +4658,8 @@
OpacityThresholdIs1AndWindowBelowThreshold_AllowsTouch) {
mDispatcher->setMaximumObscuringOpacityForTouch(1.0f);
const sp<FakeWindowHandle>& w =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4521,4 +4667,243 @@
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();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index ad93a65..a489253 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -12,6 +12,15 @@
// 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: [
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 0f5037e..69e4041 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -12,6 +12,15 @@
// 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"],
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 23893ea..d8e8b52 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -686,9 +686,9 @@
ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
if (isClientDisabledLocked(ident)) {
- ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+ ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
ident, handle);
- return INVALID_OPERATION;
+ return NO_ERROR;
}
if (info.batchParams.indexOfKey(ident) >= 0) {
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 5225dd7..9ce8d9b 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -287,29 +287,6 @@
}
}
-// TODO(b/179649922): A better algorithm to guarantee that capped connections will get a sampling
-// rate close to 200 Hz. With the current algorithm, apps might be punished unfairly: E.g.,two apps
-// make requests to the sensor service at the same time, one is not capped and uses 250 Hz, and one
-//is capped, the capped connection will only get 125 Hz.
-void SensorService::SensorEventConnection::addSensorEventsToBuffer(bool shouldResample,
- const sensors_event_t& sensorEvent, sensors_event_t* buffer, int* index) {
- if (!shouldResample || !mService->isSensorInCappedSet(sensorEvent.type)) {
- buffer[(*index)++] = sensorEvent;
- } else {
- int64_t lastTimestamp = -1;
- auto entry = mSensorLastTimestamp.find(sensorEvent.sensor);
- if (entry != mSensorLastTimestamp.end()) {
- lastTimestamp = entry->second;
- }
- // Allow 10% headroom here because the clocks are not perfect.
- if (lastTimestamp == -1 || sensorEvent.timestamp - lastTimestamp
- >= 0.9 * SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
- mSensorLastTimestamp[sensorEvent.sensor] = sensorEvent.timestamp;
- buffer[(*index)++] = sensorEvent;
- }
- }
-}
-
status_t SensorService::SensorEventConnection::sendEvents(
sensors_event_t const* buffer, size_t numEvents,
sensors_event_t* scratch,
@@ -318,8 +295,6 @@
std::unique_ptr<sensors_event_t[]> sanitizedBuffer;
- bool shouldResample = mService->isMicSensorPrivacyEnabledForUid(mUid) ||
- mIsRateCappedBasedOnPermission;
int count = 0;
Mutex::Autolock _l(mConnectionLock);
if (scratch) {
@@ -373,7 +348,7 @@
// Regular sensor event, just copy it to the scratch buffer after checking
// the AppOp.
if (hasSensorAccess() && noteOpIfRequired(buffer[i])) {
- addSensorEventsToBuffer(shouldResample, buffer[i], scratch, &count);
+ scratch[count++] = buffer[i];
}
}
i++;
@@ -383,13 +358,12 @@
buffer[i].meta_data.sensor == sensor_handle)));
}
} else {
- sanitizedBuffer.reset(new sensors_event_t[numEvents]);
- scratch = sanitizedBuffer.get();
if (hasSensorAccess()) {
- for (size_t i = 0; i < numEvents; i++) {
- addSensorEventsToBuffer(shouldResample, buffer[i], scratch, &count);
- }
+ scratch = const_cast<sensors_event_t *>(buffer);
+ count = numEvents;
} else {
+ sanitizedBuffer.reset(new sensors_event_t[numEvents]);
+ scratch = sanitizedBuffer.get();
for (size_t i = 0; i < numEvents; i++) {
if (buffer[i].type == SENSOR_TYPE_META_DATA) {
scratch[count++] = buffer[i++];
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 4e3f120..909053b 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -145,16 +145,10 @@
void capRates();
// Recover sensor connection previously capped by capRates().
void uncapRates();
-
- // Add sensorEvent to buffer at position index if the sensorEvent satisfies throttling rules.
- void addSensorEventsToBuffer(bool shouldResample, const sensors_event_t& sensorEvent,
- sensors_event_t* buffer, int* index);
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
std::atomic_bool mIsRateCappedBasedOnPermission;
- // Store a mapping of sensor to the timestamp of their last sensor event.
- std::unordered_map<int, int64_t> mSensorLastTimestamp;
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.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 942b7ae..9955cdb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2129,12 +2129,14 @@
}
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);
}
@@ -2167,7 +2169,7 @@
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);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 9c5060a..a563a60 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -242,6 +242,22 @@
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 {
// The regular operating mode where any application can register/unregister/call flush on
// sensors.
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 097f7de..4ace4c2 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -355,6 +355,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;
@@ -414,12 +421,6 @@
return true;
}
- // If the next planned present time is not current, return and try again later
- if (frameIsEarly(expectedPresentTime)) {
- ATRACE_NAME("frameIsEarly()");
- return false;
- }
-
// If this layer doesn't have a frame is shouldn't be presented
if (!hasFrameUpdate()) {
return false;
@@ -430,24 +431,6 @@
return isBufferDue(expectedPresentTime);
}
-bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
- // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
- // vsync period. We can do this change as soon as ag/13100772 is merged.
- constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
-
- const auto presentTime = nextPredictedPresentTime();
- if (!presentTime.has_value()) {
- return false;
- }
-
- if (std::abs(*presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
- return false;
- }
-
- return *presentTime >= expectedPresentTime &&
- *presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
-}
-
bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
ATRACE_CALL();
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 2118f4a..b8d3f12 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -182,17 +182,13 @@
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 final;
+ 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;
- // Returns true if the next frame is considered too early to present
- // at the given expectedPresentTime
- bool frameIsEarly(nsecs_t expectedPresentTime) const;
-
std::atomic<bool> mAutoRefresh{false};
std::atomic<bool> mSidebandStreamChanged{false};
@@ -236,13 +232,6 @@
std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-
- // Returns the predicted present time of the next frame if available
- virtual std::optional<nsecs_t> nextPredictedPresentTime() const = 0;
-
- // The amount of time SF can delay a frame if it is considered early based
- // on the VsyncModulator::VsyncConfig::appWorkDuration
- static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 3615a02..e7f373f 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -215,21 +215,6 @@
return mQueuedFrames > 0;
}
-std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime() const {
- Mutex::Autolock lock(mQueueItemLock);
- if (mQueueItems.empty()) {
- return std::nullopt;
- }
-
- const auto& bufferData = mQueueItems[0];
-
- if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
- return std::nullopt;
- }
-
- return bufferData.surfaceFrame->getPredictions().presentTime;
-}
-
status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
// This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -521,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) {
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 0ea02e1..b3b7948 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -117,8 +117,6 @@
// Temporary - Used only for LEGACY camera mode.
uint32_t getProducerStickyTransform() const;
- std::optional<nsecs_t> nextPredictedPresentTime() const override;
-
sp<BufferLayerConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index f30e1eb..a974dc4 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -41,6 +41,16 @@
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,
@@ -65,7 +75,10 @@
// 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());
+ const uint64_t bufferId = mBufferInfo.mBuffer->getId();
+ engine.unbindExternalTextureBuffer(bufferId);
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer,
+ mBufferInfo.mFence);
}
}
@@ -74,6 +87,7 @@
if (ch == nullptr) {
return OK;
}
+ ch->previousBufferId = mPreviousBufferId;
if (!ch->previousReleaseFence.get()) {
ch->previousReleaseFence = fence;
return OK;
@@ -171,6 +185,16 @@
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);
}
@@ -180,6 +204,19 @@
handle->dequeueReadyTime = dequeueReadyTime;
}
+ // 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()
@@ -247,8 +284,8 @@
}
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;
@@ -300,17 +337,17 @@
h = frame.bottom;
}
- if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
- mCurrentState.active.w == w && mCurrentState.active.h == h) {
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
+ mCurrentState.width == w && mCurrentState.height == h) {
return false;
}
if (!frame.isValid()) {
x = y = w = h = 0;
}
- mCurrentState.active.transform.set(x, y);
- mCurrentState.active.w = w;
- mCurrentState.active.h = h;
+ mCurrentState.transform.set(x, y);
+ mCurrentState.width = w;
+ mCurrentState.height = h;
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -334,8 +371,8 @@
bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& 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) {
+ std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+ const sp<ITransactionCompletedListener>& releaseBufferListener) {
ATRACE_CALL();
if (mCurrentState.buffer) {
@@ -343,7 +380,10 @@
if (mCurrentState.buffer != mDrawingState.buffer) {
// 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.
+ // dropped and we should decrement the pending buffer count and
+ // call any release buffer callbacks if set.
+ callReleaseBufferCallback(mCurrentState.releaseBufferListener, mCurrentState.buffer,
+ mCurrentState.acquireFence);
decrementPendingBufferCount();
if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
addSurfaceFrameDroppedForBuffer(mCurrentState.bufferSurfaceFrameTX);
@@ -351,9 +391,8 @@
}
}
}
-
mCurrentState.frameNumber = frameNumber;
-
+ mCurrentState.releaseBufferListener = releaseBufferListener;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
mCurrentState.modified = true;
@@ -365,14 +404,21 @@
mCurrentState.desiredPresentTime = desiredPresentTime;
mCurrentState.isAutoTimestamp = isAutoTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, isAutoTimestamp ? 0 : 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, isAutoTimestamp ? 0 : desiredPresentTime);
- if (info.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- setFrameTimelineVsyncForBufferTransaction(info, postTime);
- }
+ setFrameTimelineVsyncForBufferTransaction(info, postTime);
if (dequeueTime && *dequeueTime != 0) {
const uint64_t bufferId = buffer->getId();
@@ -607,15 +653,6 @@
return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime() const {
- const State& drawingState(getDrawingState());
- if (!drawingState.isAutoTimestamp || !drawingState.bufferSurfaceFrameTX) {
- return std::nullopt;
- }
-
- return drawingState.bufferSurfaceFrameTX->getPredictions().presentTime;
-}
-
status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
nsecs_t /*expectedPresentTime*/) {
const State& s(getDrawingState());
@@ -655,7 +692,6 @@
// are processing the next state.
addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
mDrawingState.acquireFence->getSignalTime(), latchTime);
- bufferSurfaceFrame.reset();
}
mCurrentStateModified = false;
@@ -772,7 +808,7 @@
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.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -839,10 +875,10 @@
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)),
+ return RoundedCornerState(FloatRect(static_cast<float>(s.transform.tx()),
+ static_cast<float>(s.transform.ty()),
+ static_cast<float>(s.transform.tx() + s.width),
+ static_cast<float>(s.transform.ty() + s.height)),
radius);
}
@@ -856,7 +892,7 @@
uint32_t bufferHeight = s.buffer->height;
// Undo any transformations on the buffer and return the result.
- if (s.transform & ui::Transform::ROT_90) {
+ if (s.bufferTransform & ui::Transform::ROT_90) {
std::swap(bufferWidth, bufferHeight);
}
@@ -871,29 +907,25 @@
return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
}
-void BufferStateLayer::incrementPendingBufferCount() {
- mPendingBufferTransactions++;
- tracePendingBufferCount();
-}
-
void BufferStateLayer::decrementPendingBufferCount() {
- mPendingBufferTransactions--;
- tracePendingBufferCount();
+ int32_t pendingBuffers = --mPendingBufferTransactions;
+ tracePendingBufferCount(pendingBuffers);
}
-void BufferStateLayer::tracePendingBufferCount() {
- ATRACE_INT(mBlastTransactionName.c_str(), mPendingBufferTransactions);
+void BufferStateLayer::tracePendingBufferCount(int32_t pendingBuffers) {
+ ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
}
-uint32_t BufferStateLayer::doTransaction(uint32_t flags) {
- if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer) {
+void BufferStateLayer::bufferMayChange(sp<GraphicBuffer>& newBuffer) {
+ if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer &&
+ newBuffer != mDrawingState.buffer) {
// 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.
- // This logic may not work perfectly in the face of a BufferStateLayer being the
- // deferred side of a deferred transaction, but we don't expect this use case.
+ // 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,
+ mDrawingState.acquireFence);
decrementPendingBufferCount();
}
- return Layer::doTransaction(flags);
}
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 175a40b..7a3da6f 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -55,11 +55,9 @@
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;
}
@@ -72,7 +70,8 @@
bool setBuffer(const sp<GraphicBuffer>& 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) override;
+ 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;
@@ -91,7 +90,6 @@
bool /*allowNonRectPreservingTransforms*/) override {
return false;
}
- bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
uint64_t /*frameNumber*/) override {}
void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -113,9 +111,12 @@
uint32_t getEffectiveScalingMode() const override;
// See mPendingBufferTransactions
- void incrementPendingBufferCount() override;
void decrementPendingBufferCount();
- uint32_t doTransaction(uint32_t flags) override;
+ void bufferMayChange(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;
@@ -127,7 +128,7 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- inline void tracePendingBufferCount();
+ inline void tracePendingBufferCount(int32_t pendingBuffers);
bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime);
@@ -155,8 +156,6 @@
bool bufferNeedsFiltering() const override;
- std::optional<nsecs_t> nextPredictedPresentTime() const override;
-
static const std::array<float, 16> IDENTITY_MATRIX;
std::unique_ptr<renderengine::Image> mTextureImage;
@@ -170,9 +169,14 @@
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,
@@ -184,7 +188,7 @@
// - 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
- uint64_t mPendingBufferTransactions{0};
+ std::atomic<int32_t> mPendingBufferTransactions{0};
// TODO(marissaw): support sticky transform for LEGACY camera mode
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 50bc5ed..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",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 018a687..1fd07b0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -135,6 +135,9 @@
// 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;
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 3be1cc4..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"
@@ -183,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;
@@ -264,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;
@@ -274,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 fb19216..3a84327 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -30,6 +30,8 @@
#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 -Wextra"
@@ -43,7 +45,6 @@
class CompositionEngine;
class Output;
-class LayerFE;
namespace impl {
struct OutputLayerCompositionState;
@@ -88,8 +89,9 @@
// 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.
+ virtual void writeStateToHWC(bool includeGeometry, bool skipLayer) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
@@ -115,6 +117,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/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 651230c..eeb20fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -28,10 +28,15 @@
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
@@ -48,6 +53,7 @@
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/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 8cb5ae8..f113c34 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
@@ -41,7 +42,7 @@
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
- void writeStateToHWC(bool) override;
+ void writeStateToHWC(bool includeGeometry, bool skipLayer) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -51,6 +52,7 @@
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;
@@ -66,7 +68,8 @@
private:
Rect calculateInitialCrop() const;
void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
- void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
+ bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 9a118d3..a3e84e2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -84,6 +84,14 @@
// 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 {
+ sp<GraphicBuffer> buffer = nullptr;
+ sp<Fence> acquireFence = nullptr;
+ Rect displayFrame = {};
+ ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+ } overrideInfo;
+
/*
* HWC state
*/
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..b0e42b7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -0,0 +1,141 @@
+/*
+ * 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>
+#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 sp<GraphicBuffer>& getBuffer() const { return mState->getBuffer(); }
+ 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; }
+ size_t getAge() const { return mAge; }
+ const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
+ const sp<Fence>& getDrawFence() const { return mDrawFence; }
+ ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
+
+ NonBufferHash getNonBufferHash() const;
+
+ size_t getComponentDisplayCost() const;
+ size_t getCreationCost() const;
+ size_t getDisplayCost() const;
+
+ bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) 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.setBuffer(nullptr, 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();
+ }
+ void incrementAge() { ++mAge; }
+
+ // Renders the cached set with the supplied output dataspace.
+ void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+
+ void dump(std::string& result) const;
+
+private:
+ CachedSet() = default;
+
+ NonBufferHash mFingerprint = 0;
+ std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
+ std::vector<Layer> mLayers;
+ Rect mBounds = Rect::EMPTY_RECT;
+ size_t mAge = 0;
+
+ class Texture {
+ public:
+ ~Texture() { setBuffer(nullptr, nullptr); }
+
+ void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
+ if (mRE && mBuffer) {
+ mRE->unbindExternalTextureBuffer(mBuffer->getId());
+ }
+
+ mBuffer = buffer;
+ mRE = re;
+
+ if (mRE && mBuffer) {
+ mRE->cacheExternalTextureBuffer(mBuffer);
+ }
+ }
+
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+ private:
+ sp<GraphicBuffer> mBuffer = nullptr;
+ renderengine::RenderEngine* mRE = nullptr;
+ };
+
+ Texture mTexture;
+ sp<Fence> mDrawFence;
+ ui::Dataspace mOutputDataspace;
+
+ 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..5b9a9f0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -0,0 +1,88 @@
+/*
+ * 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/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) : 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 dataspace
+ void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+
+ void reset();
+
+ 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);
+
+ void updateLayersHash();
+
+ bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
+ std::chrono::steady_clock::time_point now);
+
+ void buildCachedSets(std::chrono::steady_clock::time_point now);
+
+ Predictor& mPredictor;
+
+ ui::Size mDisplaySize;
+
+ NonBufferHash mCurrentGeometry;
+ std::chrono::steady_clock::time_point mLastGeometryUpdate;
+
+ std::vector<CachedSet> mLayers;
+ NonBufferHash mLayersHash = 0;
+ 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..a3beadc
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -0,0 +1,354 @@
+/*
+ * 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()); }
+};
+} // 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,
+ ZOrder = 1u << 4,
+ BufferTransform = 1u << 5,
+ BlendMode = 1u << 6,
+ Alpha = 1u << 7,
+ VisibleRegion = 1u << 8,
+ Dataspace = 1u << 9,
+ ColorTransform = 1u << 10,
+ CompositionType = 1u << 11,
+ SidebandStream = 1u << 12,
+ Buffer = 1u << 13,
+ SolidColor = 1u << 14,
+};
+// 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(Flags<LayerStateField> skipFields) 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&)>;
+
+ 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 Equals getDefaultEquals() {
+ return [](const T& lhs, const T& rhs) { return lhs == rhs; };
+ }
+
+ OutputLayerState(ReadFromLayerState reader,
+ ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
+ Equals equals = OutputLayerState::getDefaultEquals())
+ : mReader(reader), mToStrings(toStrings), mEquals(equals) {}
+
+ ~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);
+ 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(Flags<LayerStateField> skipFields) const override {
+ if (skipFields.test(FIELD)) {
+ return 0;
+ }
+ if (!mHash) {
+ mHash = std::hash<T>{}(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;
+ 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.
+ size_t getHash(Flags<LayerStateField> skipFields) const;
+
+ // Returns the bit-set of differing fields between this LayerState and another LayerState.
+ // This bit-set is based on NonUniqueFields only
+ Flags<LayerStateField> getDifferingFields(const LayerState& other,
+ Flags<LayerStateField> skipFields) 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(); }
+ hardware::graphics::composer::hal::Composition getCompositionType() const {
+ return mCompositionType.get();
+ }
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer.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)};
+ }};
+
+ OutputLayerState<uint32_t, LayerStateField::ZOrder> mZOrder{
+ [](auto layer) { return layer->getState().z; }};
+
+ 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; }};
+
+ // TODO(b/180638831): Generic layer metadata
+
+ // Output-dependent per-frame state
+
+ OutputLayerState<Region, LayerStateField::VisibleRegion>
+ mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+ [](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;
+ },
+ [](const Region& lhs, const Region& rhs) {
+ return lhs.hasSameRects(rhs);
+ }};
+
+ using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
+ DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
+ DataspaceState::getHalToStrings()};
+
+ // TODO(b/180638831): Buffer format
+
+ // Output-independent per-frame state
+
+ OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
+
+ // TODO(b/180638831): Surface damage
+
+ 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<sp<GraphicBuffer>, LayerStateField::Buffer>
+ mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
+ [](const sp<GraphicBuffer>& buffer) {
+ return std::vector<std::string>{base::StringPrintf("%p", buffer.get())};
+ }};
+
+ 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()};
+ }};
+
+ std::array<StateInterface*, 13> getNonUniqueFields() {
+ std::array<const StateInterface*, 13> constFields =
+ const_cast<const LayerState*>(this)->getNonUniqueFields();
+ std::array<StateInterface*, 13> fields;
+ std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
+ [](const StateInterface* constField) {
+ return const_cast<StateInterface*>(constField);
+ });
+ return fields;
+ }
+
+ std::array<const StateInterface*, 13> getNonUniqueFields() const {
+ return {
+ &mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
+ &mBlendMode, &mAlpha, &mVisibleRegion, &mOutputDataspace,
+ &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..89de34d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -0,0 +1,81 @@
+/*
+ * 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() : mFlattener(mPredictor) {}
+
+ 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&, ui::Dataspace outputDataspace);
+
+ 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/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 45891a7..dde8999 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -42,6 +42,7 @@
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
+ MOCK_CONST_METHOD0(getSequence, int32_t());
};
} // 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 95db4da..5aa53e5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -45,6 +45,7 @@
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&));
@@ -85,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());
@@ -104,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..2454ff7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -39,7 +39,7 @@
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
- MOCK_METHOD1(writeStateToHWC, void(bool));
+ MOCK_METHOD2(writeStateToHWC, void(bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
@@ -49,6 +49,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/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3907ac5..ded2dcc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -27,6 +27,9 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+#include <SurfaceFlingerProperties.sysprop.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -38,6 +41,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 +54,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>
@@ -182,6 +198,10 @@
const Rect newOrientedBounds(orientedSize);
state.orientedDisplaySpace.bounds = newOrientedBounds;
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
+
dirtyEntireOutput();
}
@@ -269,6 +289,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();
}
@@ -292,7 +321,11 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
+ const auto size = mRenderSurface->getSize();
+ editState().framebufferSpace.bounds = Rect(size);
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
dirtyEntireOutput();
}
@@ -368,13 +401,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,
@@ -632,8 +668,7 @@
}
}
-void Output::updateAndWriteCompositionState(
- const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -653,9 +688,45 @@
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;
+ }
+
+ sp<GraphicBuffer> previousOverride = nullptr;
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ bool skipLayer = false;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ if (previousOverride != nullptr &&
+ layer->getState().overrideInfo.buffer == previousOverride) {
+ ALOGV("Skipping redundant buffer");
+ skipLayer = true;
+ }
+ previousOverride = layer->getState().overrideInfo.buffer;
+ }
+
+ // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+ // only when the geometry actually changes
+ const bool includeGeometry = refreshArgs.updatingGeometryThisFrame ||
+ layer->getState().overrideInfo.buffer != nullptr || skipLayer;
+ layer->writeStateToHWC(includeGeometry, skipLayer);
}
}
@@ -826,6 +897,10 @@
chooseCompositionStrategy();
+ if (mPlanner) {
+ mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+ }
+
mRenderSurface->prepareFrame(outputState.usesClientComposition,
outputState.usesDeviceComposition);
}
@@ -1075,10 +1150,16 @@
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
.disableBlurs = disableBlurs};
- std::vector<LayerFE::LayerSettings> results =
- layerFE.prepareClientCompositionList(targetSettings);
- if (realContentIsVisible && !results.empty()) {
- layer->editState().clientCompositionTimestamp = systemTime();
+
+ std::vector<LayerFE::LayerSettings> results;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ results = layer->getOverrideCompositionList();
+ ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ } else {
+ results = layerFE.prepareClientCompositionList(targetSettings);
+ if (realContentIsVisible && !results.empty()) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ }
}
clientCompositionLayers.insert(clientCompositionLayers.end(),
@@ -1169,6 +1250,12 @@
mReleasedLayers.clear();
}
+void Output::renderCachedSets() {
+ if (mPlanner) {
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
+ }
+}
+
void Output::dirtyEntireOutput() {
auto& outputState = editState();
outputState.dirtyRegion.set(outputState.displaySpace.bounds);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0faab6f..b364649 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,7 +16,6 @@
#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>
@@ -313,7 +312,7 @@
}
}
-void OutputLayer::writeStateToHWC(bool includeGeometry) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -336,7 +335,8 @@
if (includeGeometry) {
writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
- writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+ writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
+ skipLayer);
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
@@ -352,23 +352,27 @@
HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
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) { // adyabr
+ displayFrame = outputDependentState.overrideInfo.displayFrame;
+ sourceCrop = displayFrame.toFloatRect();
}
- 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) {
@@ -389,7 +393,8 @@
}
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+ HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
error != hal::Error::NONE) {
ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
@@ -397,10 +402,12 @@
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 : 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) {
@@ -509,19 +516,26 @@
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;
+ 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));
}
}
@@ -652,6 +666,27 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
+std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+ if (getState().overrideInfo.buffer == nullptr) {
+ return {};
+ }
+
+ LayerFE::LayerSettings settings;
+ settings.geometry = renderengine::Geometry{
+ .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+ };
+ settings.bufferId = getState().overrideInfo.buffer->getId();
+ settings.source =
+ renderengine::PixelSource{.buffer = renderengine::Buffer{
+ .buffer = getState().overrideInfo.buffer,
+ .fence = getState().overrideInfo.acquireFence,
+ }};
+ 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..45dce98 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -68,6 +68,10 @@
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);
if (hwc) {
dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
new file mode 100644
index 0000000..137697b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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/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(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+
+CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
+ : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+ addLayer(layer, lastUpdate);
+}
+
+CachedSet::CachedSet(Layer layer)
+ : mFingerprint(layer.getHash()),
+ mLastUpdate(layer.getLastUpdate()),
+ mBounds(layer.getDisplayFrame()) {
+ 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();
+}
+
+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);
+ 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(std::vector<const LayerState*>::const_iterator layers) const {
+ for (const Layer& layer : mLayers) {
+ if (layer.getFramesSinceBufferUpdate() == 0) {
+ return true;
+ }
+ ++layers;
+ }
+ return false;
+}
+
+bool CachedSet::hasReadyBuffer() const {
+ return mTexture.getBuffer() != 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, ui::Dataspace outputDataspace) {
+ renderengine::DisplaySettings displaySettings{
+ .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+ .clip = mBounds,
+ .outputDataspace = outputDataspace,
+ };
+
+ Region clearRegion = Region::INVALID_REGION;
+ Rect viewport = mBounds;
+ LayerFE::ClientCompositionTargetSettings targetSettings{
+ .clip = Region(mBounds),
+ .needsFiltering = false,
+ .isSecure = true,
+ .supportsProtectedContent = false,
+ .clearRegion = clearRegion,
+ .viewport = viewport,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = true,
+ .clearContent = false,
+ .disableBlurs = false,
+ };
+
+ std::vector<renderengine::LayerSettings> layerSettings;
+ 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; });
+
+ if (sDebugHighlighLayers) {
+ renderengine::LayerSettings 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);
+ LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
+ base::unique_fd drawFence;
+
+ status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
+ base::unique_fd(), &drawFence);
+
+ if (result == NO_ERROR) {
+ mTexture.setBuffer(buffer, &renderEngine);
+ mDrawFence = new Fence(drawFence.release());
+ mOutputDataspace = outputDataspace;
+ }
+}
+
+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);
+
+ 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..30b5761
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -0,0 +1,356 @@
+/*
+ * 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 {
+
+NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash, time_point now) {
+ const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
+ mUnflattenedDisplayCost += unflattenedDisplayCost;
+
+ if (mCurrentGeometry != hash) {
+ resetActivities(hash, now);
+ mFlattenedDisplayCost += unflattenedDisplayCost;
+ return hash;
+ }
+
+ ++mInitialLayerCounts[layers.size()];
+
+ if (mergeWithCachedSets(layers, now)) {
+ hash = mLayersHash;
+ }
+
+ ++mFinalLayerCounts[mLayers.size()];
+
+ buildCachedSets(now);
+
+ return hash;
+}
+
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
+ ui::Dataspace outputDataspace) {
+ if (!mNewCachedSet) {
+ return;
+ }
+
+ mNewCachedSet->render(renderEngine, outputDataspace);
+}
+
+void Flattener::reset() {
+ resetActivities(0, std::chrono::steady_clock::now());
+
+ mUnflattenedDisplayCost = 0;
+ mFlattenedDisplayCost = 0;
+ mInitialLayerCounts.clear();
+ mFinalLayerCounts.clear();
+ mCachedSetCreationCount = 0;
+ mCachedSetCreationCost = 0;
+ mInvalidatedCachedSetAges.clear();
+}
+
+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;
+ }
+}
+
+void Flattener::updateLayersHash() {
+ size_t hash = 0;
+ for (const auto& layer : mLayers) {
+ android::hashCombineSingleHashed(hash, layer.getNonBufferHash());
+ }
+ mLayersHash = hash;
+}
+
+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(LayerStateField::Buffer)) {
+ if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) {
+ 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) {
+ 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(),
+ };
+ ++incomingLayerIter;
+ }
+
+ if (currentLayerIter->getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ }
+ ++currentLayerIter;
+
+ skipCount -= layerCount;
+ }
+ merged.emplace_back(std::move(*mNewCachedSet));
+ mNewCachedSet = std::nullopt;
+ continue;
+ }
+ }
+
+ if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) {
+ currentLayerIter->incrementAge();
+ merged.emplace_back(*currentLayerIter);
+
+ // Skip the incoming layers corresponding to this valid current layer
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ 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(),
+ };
+ ++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);
+ updateLayersHash();
+ 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;
+ 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 {
+ // Runs must be at least 2 sets long or there's nothing to combine
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ 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);
+ }
+
+ // 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..222b2be
--- /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(
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ size_t hash = 0;
+ for (const StateInterface* field : getNonUniqueFields()) {
+ android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+ }
+
+ return hash;
+}
+
+Flags<LayerStateField> LayerState::getDifferingFields(
+ const LayerState& other,
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ Flags<LayerStateField> differences;
+ auto myFields = getNonUniqueFields();
+ auto otherFields = other.getNonUniqueFields();
+ for (size_t i = 0; i < myFields.size(); ++i) {
+ if (skipFields.test(myFields[i]->getField())) {
+ 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.mZOrder == rhs.mZOrder &&
+ lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
+ lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mOutputDataspace == rhs.mOutputDataspace &&
+ 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(LayerStateField::Buffer));
+ }
+
+ 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..87721c7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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/LayerFECompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+namespace android::compositionengine::impl::planner {
+
+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) {
+ const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.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,
+ ui::Dataspace outputDataspace) {
+ mFlattener.renderCachedSets(renderEngine, outputDataspace);
+}
+
+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..07920b8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -0,0 +1,483 @@
+/*
+ * 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(LayerStateField::Buffer) ==
+ other[i]->getHash(LayerStateField::Buffer)) {
+ 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], LayerStateField::Buffer);
+
+ // 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/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dcfc162..9dd199d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -840,19 +840,19 @@
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -861,7 +861,7 @@
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -891,7 +891,7 @@
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
expectSetColorCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -901,7 +901,7 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -911,7 +911,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -921,7 +921,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -934,7 +934,7 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -944,7 +944,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -956,7 +956,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -969,7 +969,7 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -980,7 +980,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c4ae3a7..5f0b0ee 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -96,6 +96,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 +113,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;
@@ -751,7 +755,9 @@
mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
@@ -766,7 +772,9 @@
injectOutputLayer(layer3);
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
@@ -775,11 +783,14 @@
InjectedLayer layer3;
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));
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));
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));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -791,7 +802,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) {
@@ -800,11 +813,14 @@
InjectedLayer layer3;
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));
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));
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));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -815,7 +831,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = true;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
@@ -824,11 +842,14 @@
InjectedLayer layer3;
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));
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));
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));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -839,7 +860,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = true;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
/*
@@ -877,6 +900,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();
@@ -1621,14 +1647,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;
@@ -1639,13 +1668,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);
}
@@ -1665,12 +1697,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;
};
@@ -2680,12 +2712,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;
};
@@ -2761,11 +2793,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();
@@ -2792,9 +2824,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();
}
@@ -3298,12 +3330,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;
};
@@ -3461,7 +3493,8 @@
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));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
@@ -3476,7 +3509,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);
@@ -3484,7 +3519,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);
@@ -3509,12 +3546,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;
@@ -3608,11 +3645,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})));
@@ -3646,7 +3683,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));
@@ -3672,7 +3709,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));
@@ -3736,9 +3773,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 */,
@@ -3798,11 +3835,11 @@
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>(
@@ -3854,11 +3891,11 @@
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>(
@@ -3911,11 +3948,11 @@
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>(
@@ -3966,11 +4003,11 @@
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>(
@@ -4019,11 +4056,11 @@
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 */,
@@ -4038,11 +4075,14 @@
// 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));
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));
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));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4055,7 +4095,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
@@ -4065,11 +4107,14 @@
// 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));
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));
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));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);
@@ -4083,7 +4128,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
@@ -4141,7 +4188,7 @@
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{
@@ -4159,7 +4206,7 @@
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;
@@ -4199,7 +4246,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 */,
@@ -4239,7 +4286,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, mLayers[2].mLayerSettings})));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
new file mode 100644
index 0000000..377f817
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -0,0 +1,325 @@
+/*
+ * 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/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;
+
+ 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->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));
+ }
+}
+
+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_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());
+ 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);
+
+ std::vector<const LayerState*> incomingLayers = {
+ layer1.getState(),
+ layer2.getState(),
+ layer3.getState(),
+ };
+
+ EXPECT_FALSE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+}
+
+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();
+
+ std::vector<const LayerState*> incomingLayers = {
+ layer1.getState(),
+ layer2.getState(),
+ layer3.getState(),
+ };
+
+ EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+}
+
+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());
+ 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) {
+ CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[1]->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 sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*) -> size_t {
+ EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip);
+ 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));
+ EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
+ cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+ expectReadyBuffer(cachedSet);
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
+ 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..bd77559
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -0,0 +1,450 @@
+/*
+ * 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/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;
+};
+
+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->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));
+ }
+}
+
+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, ui::Dataspace::SRGB);
+
+ // same geometry, update the internal layer stack
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+}
+
+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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+}
+
+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, ui::Dataspace::SRGB);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ 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));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ 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, ui::Dataspace::SRGB);
+
+ 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));
+ mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+ 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..8f235ab
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -0,0 +1,1020 @@
+/*
+ * 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>
+
+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 int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+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.43, 0.2f);
+
+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));
+ }
+
+ 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.
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Id),
+ otherLayerState->getHash(LayerStateField::Id));
+
+ // Similarly, Id cannot be included in differing fields.
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Id));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Id));
+
+ 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.
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Name),
+ otherLayerState->getHash(LayerStateField::Name));
+
+ // Similarly, Name cannot be included in differing fields.
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Name));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Name));
+
+ 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());
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::DisplayFrame),
+ otherLayerState->getHash(LayerStateField::DisplayFrame));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::DisplayFrame));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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());
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::CompositionType),
+ otherLayerState->getHash(LayerStateField::CompositionType));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::CompositionType));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::CompositionType));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getBuffer) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer = new GraphicBuffer();
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(layerFECompositionState.buffer, mLayerState->getBuffer());
+}
+
+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(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
+ 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);
+
+ EXPECT_NE(mLayerState->getBuffer(), otherLayerState->getBuffer());
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Buffer),
+ otherLayerState->getHash(LayerStateField::Buffer));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Buffer));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Buffer));
+
+ // 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::SourceCrop),
+ otherLayerState->getHash(LayerStateField::SourceCrop));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SourceCrop));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SourceCrop));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateZOrder) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.z = sZOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.z = sZTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder), updates);
+}
+
+TEST_F(LayerStateTest, compareZOrder) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.z = sZOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ outputLayerCompositionStateTwo.z = sZTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionState);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::ZOrder),
+ otherLayerState->getHash(LayerStateField::ZOrder));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ZOrder));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ZOrder));
+
+ 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::BufferTransform),
+ otherLayerState->getHash(LayerStateField::BufferTransform));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BufferTransform));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::BlendMode),
+ otherLayerState->getHash(LayerStateField::BlendMode));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BlendMode));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Alpha),
+ otherLayerState->getHash(LayerStateField::Alpha));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Alpha));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Alpha));
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::VisibleRegion),
+ otherLayerState->getHash(LayerStateField::VisibleRegion));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::VisibleRegion));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::Dataspace),
+ otherLayerState->getHash(LayerStateField::Dataspace));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Dataspace));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Dataspace));
+
+ 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::ColorTransform),
+ otherLayerState->getHash(LayerStateField::ColorTransform));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ColorTransform));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::SidebandStream),
+ otherLayerState->getHash(LayerStateField::SidebandStream));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SidebandStream));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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);
+
+ EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+ otherLayerState->getHash(LayerStateField::None));
+ EXPECT_EQ(mLayerState->getHash(LayerStateField::SolidColor),
+ otherLayerState->getHash(LayerStateField::SolidColor));
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SolidColor));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+ otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+ otherLayerState->getDifferingFields(*mLayerState, 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
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
new file mode 100644
index 0000000..43e119f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -0,0 +1,528 @@
+/*
+ * 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 int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+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,
+ .z = sZOne,
+ };
+ LayerFECompositionState layerFECompositionStateOne;
+ layerFECompositionStateOne.alpha = sAlphaOne;
+ layerFECompositionStateOne.colorTransformIsIdentity = true;
+ 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,
+ .z = sZTwo,
+ };
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.alpha = sAlphaTwo;
+ layerFECompositionStateTwo.colorTransformIsIdentity = false;
+ layerFECompositionStateTwo.colorTransform = sMat4One;
+ 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,
+ .z = sZOne,
+ };
+ 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,
+ .z = sZTwo,
+ };
+ 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(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
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 36c4c4d..b7b2cc6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -145,9 +145,9 @@
}
void DisplayDevice::setActiveMode(DisplayModeId id) {
- LOG_FATAL_IF(id.value() >= mSupportedModes.size(),
- "Cannot set active mode which is not supported.");
- mActiveModeId = id;
+ const auto mode = getMode(id);
+ LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
+ mActiveMode = mode;
}
status_t DisplayDevice::initiateModeChange(DisplayModeId modeId,
@@ -164,7 +164,7 @@
}
const DisplayModePtr& DisplayDevice::getActiveMode() const {
- return mSupportedModes[mActiveModeId.value()];
+ return mActiveMode;
}
const DisplayModes& DisplayDevice::getSupportedModes() const {
@@ -172,9 +172,10 @@
}
DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
- const auto id = modeId.value();
- if (static_cast<size_t>(id) < mSupportedModes.size()) {
- return mSupportedModes[id];
+ const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
+ [&](DisplayModePtr mode) { return mode->getId() == modeId; });
+ if (it != mSupportedModes.end()) {
+ return *it;
}
return nullptr;
}
@@ -353,8 +354,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 a94bfa2..68846d3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -126,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;
@@ -208,7 +210,7 @@
hardware::graphics::composer::hal::PowerMode mPowerMode =
hardware::graphics::composer::hal::PowerMode::OFF;
- DisplayModeId mActiveModeId;
+ DisplayModePtr mActiveMode;
const DisplayModes mSupportedModes;
std::atomic<nsecs_t> mLastHwVsync = 0;
@@ -217,6 +219,8 @@
const bool mIsPrimary;
std::optional<DeviceProductInfo> mDeviceProductInfo;
+
+ std::vector<ui::Hdr> mOverrideHdrTypes;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 853c05b..85cc993 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -125,6 +125,12 @@
// 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) {}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cf6bc68..f532e50 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -91,6 +91,12 @@
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();
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/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index c7dbf88..0bc2d3e 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -18,14 +18,16 @@
#define LOG_TAG "FpsReporter"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "FpsReporter.h"
+#include <algorithm>
+#include "FpsReporter.h"
#include "Layer.h"
+#include "SurfaceFlinger.h"
namespace android {
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline)
- : mFrameTimeline(frameTimeline) {}
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger)
+ : mFrameTimeline(frameTimeline), mFlinger(flinger) {}
void FpsReporter::dispatchLayerFps() const {
std::vector<TrackedListener> localListeners;
@@ -41,16 +43,33 @@
});
}
- for (const auto& listener : localListeners) {
- sp<Layer> promotedLayer = listener.layer.promote();
- if (promotedLayer != nullptr) {
- std::unordered_set<int32_t> layerIds;
+ std::unordered_set<int32_t> seenTasks;
+ std::vector<std::pair<TrackedListener, sp<Layer>>> listenersAndLayersToReport;
- promotedLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
-
- listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+ 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));
}
}
@@ -59,11 +78,11 @@
mListeners.erase(who);
}
-void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer) {
+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, layer});
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, taskId});
}
void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index d64b3dc..1cec295 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -27,10 +27,11 @@
namespace android {
class Layer;
+class SurfaceFlinger;
class FpsReporter : public IBinder::DeathRecipient {
public:
- FpsReporter(frametimeline::FrameTimeline& frameTimeline);
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger);
// Dispatches updated layer fps values for the registered listeners
// This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
@@ -41,7 +42,7 @@
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, const wp<Layer>& layer);
+ void addListener(const sp<gui::IFpsListener>& listener, int32_t taskId);
// Deregisters an Fps listener
void removeListener(const sp<gui::IFpsListener>& listener);
@@ -55,10 +56,11 @@
struct TrackedListener {
sp<gui::IFpsListener> listener;
- wp<Layer> layer;
+ int32_t taskId;
};
frametimeline::FrameTimeline& mFrameTimeline;
+ SurfaceFlinger& mFlinger;
std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 1e6d21e..10a5833 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -1,3 +1,12 @@
+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"],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index ff000c9..2784861 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -207,6 +207,17 @@
}
}
+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;
@@ -285,7 +296,7 @@
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter)
+ TraceCookieCounter* traceCookieCounter, bool isBuffer)
: mToken(frameTimelineInfo.vsyncId),
mInputEventId(frameTimelineInfo.inputEventId),
mOwnerPid(ownerPid),
@@ -299,7 +310,8 @@
mActuals({0, 0, 0}),
mTimeStats(timeStats),
mJankClassificationThresholds(thresholds),
- mTraceCookieCounter(*traceCookieCounter) {}
+ mTraceCookieCounter(*traceCookieCounter),
+ mIsBuffer(isBuffer) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::scoped_lock lock(mMutex);
@@ -336,6 +348,11 @@
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) {
@@ -384,6 +401,20 @@
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());
@@ -396,6 +427,8 @@
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",
@@ -433,37 +466,41 @@
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
-void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
- nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
+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;
+}
- if (mPresentState != PresentState::Presented) {
- // No need to update dropped buffers
+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;
}
- mActuals.presentTime = presentTime;
- // Jank Analysis for SurfaceFrame
if (mPredictionState == PredictionState::None) {
// Cannot do jank classification on frames that don't have a token.
return;
}
- if (mPredictionState == PredictionState::Expired) {
- // 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;
- mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
- mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
- const constexpr nsecs_t kAppDeadlineDelta = -1;
- mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
- mJankType, displayDeadlineDelta, displayPresentDelta,
- kAppDeadlineDelta});
- return;
- }
+ deadlineDelta = mActuals.endTime - mPredictions.endTime;
const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
- const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
: 0;
@@ -504,6 +541,17 @@
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
@@ -511,14 +559,18 @@
// Propagate displayFrame's jank if it exists
mJankType |= displayFrameJankType;
} else {
- 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;
+ 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) {
@@ -532,8 +584,28 @@
}
}
}
- mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName, mJankType,
- displayDeadlineDelta, displayPresentDelta, deadlineDelta});
+}
+
+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 {
@@ -613,6 +685,7 @@
FrameReadyMetadata::OnTimeFinish);
actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+ actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
});
// Actual timeline end
@@ -683,13 +756,14 @@
}
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds)
+ JankClassificationThresholds thresholds, nsecs_t hwcDuration)
: mMaxDisplayFrames(kDefaultMaxDisplayFrames),
mTimeStats(std::move(timeStats)),
mSurfaceFlingerPid(surfaceFlingerPid),
- mJankClassificationThresholds(thresholds) {
- mCurrentDisplayFrame =
- std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds(thresholds),
+ mHwcDuration(hwcDuration) {
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, thresholds, hwcDuration,
+ &mTraceCookieCounter);
}
void FrameTimeline::onBootFinished() {
@@ -707,13 +781,14 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName) {
+ 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);
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
}
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
@@ -722,21 +797,24 @@
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter);
+ &mTraceCookieCounter, isBuffer);
}
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
}
FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
+ nsecs_t hwcDuration,
TraceCookieCounter* traceCookieCounter)
: mSurfaceFlingerPredictions(TimelineItem()),
mSurfaceFlingerActuals(TimelineItem()),
mTimeStats(timeStats),
mJankClassificationThresholds(thresholds),
+ mHwcDuration(hwcDuration),
mTraceCookieCounter(*traceCookieCounter) {
mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
}
@@ -755,10 +833,14 @@
}
void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
- const std::shared_ptr<FenceTime>& presentFence) {
+ const std::shared_ptr<FenceTime>& presentFence,
+ bool gpuComposition) {
ATRACE_CALL();
std::scoped_lock lock(mMutex);
mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
+ if (gpuComposition) {
+ mCurrentDisplayFrame->setGpuComposition();
+ }
mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
flushPendingPresentFences();
finalizeCurrentDisplayFrame();
@@ -796,25 +878,32 @@
mSurfaceFlingerActuals.endTime = actualEndTime;
}
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
- mSurfaceFlingerActuals.presentTime = signalTime;
- if (mPredictionState == PredictionState::Expired) {
- // Cannot do jank classification with expired predictions
+void FrameTimeline::DisplayFrame::setGpuComposition() {
+ mGpuComposition = true;
+}
+
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
+ if (mPredictionState == PredictionState::Expired ||
+ mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+ // Cannot do jank classification with expired predictions or invalid signal times.
mJankType = JankType::Unknown;
+ deadlineDelta = -1;
+ deltaToVsync = -1;
return;
}
// Delta between the expected present and the actual present
const nsecs_t presentDelta =
mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
- const nsecs_t deadlineDelta =
- mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime;
+ deadlineDelta =
+ mSurfaceFlingerActuals.endTime - (mSurfaceFlingerPredictions.endTime - mHwcDuration);
// How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
// was a prediction error or not.
- nsecs_t deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+ deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
: 0;
+
if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
@@ -822,8 +911,9 @@
mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
}
- if (mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime >
- mJankClassificationThresholds.deadlineThreshold) {
+ if (mSurfaceFlingerActuals.endTime > mSurfaceFlingerPredictions.endTime - mHwcDuration) {
+ // SF needs to have finished at least mHwcDuration ahead of the deadline for it to be
+ // on time.
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
} else {
mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
@@ -875,8 +965,13 @@
mJankType = JankType::PredictionError;
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- // Finish late, Present late
- mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ if (mFrameStartMetadata == FrameStartMetadata::LateStart) {
+ // Late start, Late finish, Late Present
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // OnTime start, Finish late, Present late
+ mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ }
} else {
// Finish time unknown
mJankType = JankType::Unknown;
@@ -886,6 +981,14 @@
mJankType = JankType::Unknown;
}
}
+}
+
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+ mSurfaceFlingerActuals.presentTime = signalTime;
+ nsecs_t deadlineDelta = 0;
+ nsecs_t deltaToVsync = 0;
+ classifyJank(deadlineDelta, deltaToVsync);
+
for (auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
}
@@ -944,13 +1047,14 @@
FrameReadyMetadata::OnTimeFinish);
actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition);
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.endTime));
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime));
auto* event = packet->set_frame_timeline_event();
auto* actualDisplayFrameEndEvent = event->set_frame_end();
@@ -1047,11 +1151,9 @@
continue;
}
}
- if (signalTime != Fence::SIGNAL_TIME_INVALID) {
- auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime);
- displayFrame->trace(mSurfaceFlingerPid);
- }
+ auto& displayFrame = pendingPresentFence.second;
+ displayFrame->onPresent(signalTime);
+ displayFrame->trace(mSurfaceFlingerPid);
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
--i;
@@ -1066,7 +1168,7 @@
mDisplayFrames.push_back(mCurrentDisplayFrame);
mCurrentDisplayFrame.reset();
mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter);
+ mHwcDuration, &mTraceCookieCounter);
}
nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index d65769b..3f04592 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -159,7 +159,7 @@
int32_t layerId, std::string layerName, std::string debugName,
PredictionState predictionState, TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter);
+ TraceCookieCounter* traceCookieCounter, bool isBuffer);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -180,6 +180,11 @@
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.
@@ -192,6 +197,8 @@
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.
@@ -206,6 +213,7 @@
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.
@@ -216,6 +224,8 @@
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;
@@ -251,6 +261,9 @@
// 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;
};
/*
@@ -270,7 +283,7 @@
// 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) = 0;
+ 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.
@@ -280,11 +293,11 @@
// 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.
- virtual void setSfPresent(nsecs_t sfPresentTime,
- const std::shared_ptr<FenceTime>& presentFence) = 0;
+ // Sets the sfPresentTime, gpuComposition 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.
+ virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
+ bool gpuComposition) = 0;
// Args:
// -jank : Dumps only the Display Frames that are either janky themselves
@@ -342,7 +355,7 @@
class DisplayFrame {
public:
DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter);
+ nsecs_t hwcDuration, TraceCookieCounter* traceCookieCounter);
virtual ~DisplayFrame() = default;
// Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
// SurfaceFrame is janky.
@@ -355,7 +368,7 @@
// 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, classifies the jank and returns the classified jankType.
+ // Sets the appropriate metadata and classifies the jank.
void onPresent(nsecs_t signalTime);
// Adds the provided SurfaceFrame to the current display frame.
void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
@@ -363,6 +376,7 @@
void setPredictions(PredictionState predictionState, TimelineItem predictions);
void setActualStartTime(nsecs_t actualStartTime);
void setActualEndTime(nsecs_t actualEndTime);
+ void setGpuComposition();
// BaseTime is the smallest timestamp in a DisplayFrame.
// Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -371,6 +385,7 @@
// 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; }
@@ -382,6 +397,7 @@
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);
int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
@@ -394,6 +410,7 @@
TimelineItem mSurfaceFlingerActuals;
std::shared_ptr<TimeStats> mTimeStats;
const JankClassificationThresholds mJankClassificationThresholds;
+ const nsecs_t mHwcDuration;
// Collection of predictions and actual values sent over by Layers
std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
@@ -419,17 +436,18 @@
};
FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds = {});
+ JankClassificationThresholds thresholds = {},
+ nsecs_t hwcDuration = kDefaultHwcDuration);
~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) override;
+ 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) override;
+ void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
+ bool gpuComposition = false) 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;
@@ -464,6 +482,11 @@
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
const JankClassificationThresholds mJankClassificationThresholds;
+ // In SF Predictions, both end & present are the same. The predictions consider the time used by
+ // composer as well, but we have no way to estimate how much time the composer needs. We are
+ // assuming an arbitrary time for the composer work.
+ const nsecs_t mHwcDuration;
+ static constexpr nsecs_t kDefaultHwcDuration = std::chrono::nanoseconds(3ms).count();
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
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 937868a..cd3e8ad 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -102,18 +102,18 @@
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);
@@ -949,13 +949,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
@@ -1049,6 +1048,9 @@
c.callbackHandles.push_back(handle);
}
+ // Allow BufferStateLayer to release any unlatched buffers in drawing state.
+ bufferMayChange(c.buffer);
+
// Commit the transaction
commitTransaction(c);
mPendingStatesSnapshot = mPendingStates;
@@ -1058,6 +1060,15 @@
}
void Layer::commitTransaction(State& stateToCommit) {
+ if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+ 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
@@ -1326,11 +1337,11 @@
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);
@@ -1339,7 +1350,6 @@
bool Layer::setMetadata(const LayerMetadata& data) {
if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
- mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1538,6 +1548,7 @@
// Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
mCurrentState.bufferSurfaceFrameTX = it->second;
mCurrentState.bufferlessSurfaceFramesTX.erase(it);
+ mCurrentState.bufferSurfaceFrameTX->promoteToBuffer();
mCurrentState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
} else {
mCurrentState.bufferSurfaceFrameTX =
@@ -1597,7 +1608,8 @@
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
- mTransactionName);
+ mTransactionName,
+ /*isBuffer*/ false);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
surfaceFrame->setAcquireFenceTime(postTime);
@@ -1613,7 +1625,8 @@
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
- getSequence(), mName, debugName);
+ getSequence(), mName, debugName,
+ /*isBuffer*/ true);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -1733,7 +1746,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();
@@ -2432,7 +2445,7 @@
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);
@@ -2461,11 +2474,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));
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 664c10b..1c5d6ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -214,8 +214,8 @@
bool modified;
// Crop is expressed in layer space coordinate.
- Rect crop_legacy;
- Rect requestedCrop_legacy;
+ Rect crop;
+ Rect requestedCrop;
// If set, defers this state update until the identified Layer
// receives a frame with the given frameNumber
@@ -250,12 +250,13 @@
// 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;
@@ -311,6 +312,7 @@
// 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
@@ -421,7 +423,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?
@@ -460,13 +462,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*/,
bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */,
- const FrameTimelineInfo& /*info*/) {
+ const FrameTimelineInfo& /*info*/,
+ const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
return false;
};
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -503,8 +505,6 @@
virtual void useEmptyDamage() {}
Region getVisibleRegion(const DisplayDevice*) const;
- virtual void incrementPendingBufferCount() {}
-
/*
* isOpaque - true if this surface is opaque
*
@@ -549,7 +549,7 @@
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
@@ -724,7 +724,7 @@
// Returns the bounds of the layer without any buffer scaling.
FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
- int32_t getSequence() const { return sequence; }
+ int32_t getSequence() const override { return sequence; }
// For tracing.
// TODO: Replace with raw buffer id from buffer metadata when that becomes available.
@@ -776,6 +776,12 @@
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(sp<GraphicBuffer>& /* newBuffer */){};
+
+ /*
* Remove relative z for the layer if its relative parent is not part of the
* provided layer tree.
*/
@@ -946,6 +952,9 @@
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
+ virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
+ virtual std::string getPendingBufferCounterName() { return ""; }
+
protected:
class SyncPoint {
public:
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index 053b7f7..1c0263b 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -80,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/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index b29c624..1d00cc3 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -241,7 +241,8 @@
auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{});
+ std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+ nullptr /* releaseBufferListener */);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
@@ -254,7 +255,8 @@
auto buffer = buffers[mFrame];
mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{});
+ std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+ nullptr /* releaseBufferListener */);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 09615f9..d0032ac 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -176,7 +176,7 @@
mScheduler(scheduler),
mTunables(tunables),
mIdleTimer(
- "RegionSamplingIdleTimer",
+ "RegSampIdle",
std::chrono::duration_cast<std::chrono::milliseconds>(
mTunables.mSamplingTimerTimeout),
[] {}, [this] { checkForStaleLuma(); }),
@@ -184,7 +184,7 @@
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();
}
@@ -481,6 +481,12 @@
// 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.
+ if (mCachedBuffer != nullptr && mCachedBuffer != buffer) {
+ if (mFlinger.getRenderEngine().getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
+ mFlinger.getRenderEngine().unbindExternalTextureBuffer(mCachedBuffer->getId());
+ }
+ }
mCachedBuffer = buffer;
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ba43e70..6553efe 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -354,7 +354,7 @@
return mTokenManager->generateTokenForPredictions(
{timestamp, deadlineTimestamp, expectedVSyncTimestamp});
}
- return static_cast<int64_t>(0);
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
}();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
@@ -494,10 +494,16 @@
const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
const auto deadlineTimestamp = now + timeout.count();
const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
- // TODO(b/162890590): use TokenManager to populate vsyncId
+ 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,
- deadlineTimestamp, /*vsyncId=*/0));
+ deadlineTimestamp, vsyncId));
}
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index de11c16..91e8043 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -610,15 +610,16 @@
void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
DisplayModeId currentModeId) {
std::lock_guard lock(mLock);
- LOG_ALWAYS_FATAL_IF(modes.empty());
- LOG_ALWAYS_FATAL_IF(currentModeId.value() >= modes.size());
+ // 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();
- const auto fps = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
mRefreshRates.emplace(modeId,
- std::make_unique<RefreshRate>(modeId, mode, fps,
+ std::make_unique<RefreshRate>(modeId, mode, mode->getFps(),
RefreshRate::ConstructorTag(0)));
if (modeId == currentModeId) {
mCurrentRefreshRate = mRefreshRates.at(modeId).get();
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 1f821be..194d808 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -50,24 +50,23 @@
TransactionSchedule schedule) {
switch (schedule) {
case Schedule::EarlyStart:
- ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
- mExplicitEarlyWakeup = true;
+ ALOGW_IF(mEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+ mEarlyWakeup = true;
break;
case Schedule::EarlyEnd:
- ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
- mExplicitEarlyWakeup = false;
+ ALOGW_IF(!mEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+ mEarlyWakeup = false;
break;
- case Schedule::Early:
case Schedule::Late:
- // No change to mExplicitEarlyWakeup for non-explicit states.
+ // No change to mEarlyWakeup for non-explicit states.
break;
}
if (mTraceDetailedInfo) {
- ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ ATRACE_INT("mEarlyWakeup", mEarlyWakeup);
}
- if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) {
+ if (!mEarlyWakeup && schedule == Schedule::EarlyEnd) {
mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
mEarlyTransactionStartTime = mNow();
}
@@ -129,8 +128,8 @@
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 (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd ||
- mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
+ if (mEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd || mEarlyTransactionFrames > 0 ||
+ mRefreshRateChangePending) {
return mVsyncConfigSet.early;
} else if (mEarlyGpuFrames > 0) {
return mVsyncConfigSet.earlyGpu;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 355a14a..fcde279 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -30,7 +30,6 @@
// fixed number of frames, respectively.
enum class TransactionSchedule {
Late, // Default.
- Early, // Deprecated.
EarlyStart,
EarlyEnd
};
@@ -114,7 +113,7 @@
using Schedule = TransactionSchedule;
std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
- std::atomic<bool> mExplicitEarlyWakeup = false;
+ std::atomic<bool> mEarlyWakeup = false;
std::atomic<bool> mRefreshRateChangePending = false;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6a98e36..36b550b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -23,6 +23,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceFlinger.h"
+#include "TraceUtils.h"
#include <android-base/properties.h>
#include <android/configuration.h>
@@ -79,7 +80,6 @@
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Timers.h>
-#include <utils/Trace.h>
#include <utils/misc.h>
#include <algorithm>
@@ -104,6 +104,7 @@
#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"
@@ -131,6 +132,7 @@
#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)
@@ -439,10 +441,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;
@@ -496,6 +494,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();
@@ -909,11 +910,6 @@
}
info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
- if (display->isPrimary()) {
- if (const auto mode = getDesiredActiveMode()) {
- info->activeDisplayModeId = static_cast<int32_t>(mode->modeId.value());
- }
- }
const auto& supportedModes = display->getSupportedModes();
info->supportedDisplayModes.clear();
@@ -1291,6 +1287,21 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
+ const std::vector<ui::Hdr>& hdrTypes) {
+ Mutex::Autolock lock(mStateLock);
+
+ auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+
+ display->overrideHdrTypes(hdrTypes);
+ dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
@@ -1428,14 +1439,12 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::addFpsListener(const sp<IBinder>& layerHandle,
- const sp<gui::IFpsListener>& listener) {
+status_t SurfaceFlinger::addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
if (!listener) {
return BAD_VALUE;
}
- const wp<Layer> layer = fromHandle(layerHandle);
- mFpsReporter->addListener(listener, layer);
+ mFpsReporter->addListener(listener, taskId);
return NO_ERROR;
}
@@ -1464,14 +1473,16 @@
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 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 ftl::yield<status_t>(NAME_NOT_FOUND);
@@ -1689,7 +1700,12 @@
}
void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
- ATRACE_CALL();
+ 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(vsyncId, expectedVSyncTime);
@@ -1876,7 +1892,12 @@
// 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();
}
}
@@ -2105,7 +2126,8 @@
// 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(),
- std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+ std::make_shared<FenceTime>(mPreviousPresentFences[0]),
+ glCompositionDoneFenceTime != FenceTime::NO_FENCE);
nsecs_t dequeueReadyTime = systemTime();
for (const auto& layer : mLayersWithQueuedFrames) {
@@ -2303,22 +2325,74 @@
// here the transaction has been committed
}
-DisplayModes SurfaceFlinger::loadSupportedDisplayModes(PhysicalDisplayId displayId) const {
- const auto hwcModes = getHwComposer().getModes(displayId);
- DisplayModes modes;
- int32_t nextModeId = 0;
- for (const auto& hwcMode : hwcModes) {
- modes.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());
+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();
}
- return modes;
+
+ 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() {
@@ -2334,15 +2408,9 @@
const auto it = mPhysicalDisplayTokens.find(displayId);
if (event.connection == hal::Connection::CONNECTED) {
- auto supportedModes = loadSupportedDisplayModes(displayId);
- const auto activeModeHwcId = getHwComposer().getActiveMode(displayId);
- LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
-
- const auto activeMode = *std::find_if(supportedModes.begin(), supportedModes.end(),
- [activeModeHwcId](const DisplayModePtr& mode) {
- return mode->getHwcId() == *activeModeHwcId;
- });
- // TODO(b/175678215) Handle the case when activeMode is not in supportedModes
+ DisplayModes supportedModes;
+ DisplayModePtr activeMode;
+ loadDisplayModes(displayId, supportedModes, activeMode);
if (it == mPhysicalDisplayTokens.end()) {
ALOGV("Creating display %s", to_string(displayId).c_str());
@@ -2411,7 +2479,6 @@
const sp<IGraphicBufferProducer>& producer) {
DisplayDeviceCreationArgs creationArgs(this, getHwComposer(), displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
- creationArgs.hwComposer = getHwComposer();
creationArgs.isSecure = state.isSecure;
creationArgs.displaySurface = displaySurface;
creationArgs.hasWideColorGamut = false;
@@ -2925,7 +2992,7 @@
mRefreshRateConfigs = std::make_unique<
scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
displayState.physical->activeMode->getId(),
- android::sysprop::enable_frame_rate_override(true));
+ 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);
@@ -2956,7 +3023,7 @@
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
- mFpsReporter = new FpsReporter(*mFrameTimeline);
+ 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.
@@ -2993,9 +3060,8 @@
void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- mTransactionPending = false;
+ signalSynchronousTransactions();
mAnimTransactionPending = false;
- mTransactionCV.broadcast();
}
void SurfaceFlinger::commitTransactionLocked() {
@@ -3264,16 +3330,16 @@
auto& [applyToken, transactionQueue] = *it;
while (!transactionQueue.empty()) {
- const auto& transaction = transactionQueue.front();
- if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
+ auto& transaction = transactionQueue.front();
+ if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+ transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states,
- false /* updateTransactionCounters*/,
+ transaction.originUid, transaction.states,
pendingBuffers)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
- transactions.push_back(transaction);
+ transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
}
@@ -3290,23 +3356,21 @@
// Case 2: push to pending when there exist a pending queue.
// Case 3: others are ready to apply.
while (!mTransactionQueue.empty()) {
- const auto& transaction = mTransactionQueue.front();
+ auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
- // Call transactionIsReadyToBeApplied first in case we need to
- // incrementPendingBufferCount and keep track of pending buffers
- // if the transaction contains a buffer.
- if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
+ if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+ transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states,
- true /* updateTransactionCounters */,
+ transaction.originUid, transaction.states,
pendingBuffers) ||
pendingTransactions) {
- mPendingTransactionQueues[transaction.applyToken].push(transaction);
+ mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
- transactions.push_back(transaction);
+ transactions.emplace_back(std::move(transaction));
}
mTransactionQueue.pop();
+ ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
}
@@ -3319,41 +3383,81 @@
transaction.postTime, transaction.permissions,
transaction.hasListenerCallbacks, transaction.listenerCallbacks,
transaction.originPid, transaction.originUid, transaction.id);
+ if (transaction.transactionCommittedSignal) {
+ mTransactionCommittedSignals.emplace_back(
+ std::move(transaction.transactionCommittedSignal));
+ }
}
}
}
bool SurfaceFlinger::transactionFlushNeeded() {
Mutex::Autolock _l(mQueueLock);
- return !mPendingTransactionQueues.empty();
+ 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;
+
+ 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(
- bool isAutoTimestamp, int64_t desiredPresentTime, const Vector<ComposerState>& states,
- bool updateTransactionCounters,
+ const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+ ATRACE_CALL();
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
bool ready = true;
// 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 (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ ATRACE_NAME("not current");
+ ready = false;
+ }
+
+ if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+ ATRACE_NAME("!isVsyncValid");
+ ready = 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");
ready = false;
}
for (const ComposerState& state : states) {
const layer_state_t& s = state.state;
- if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
- continue;
- }
- if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
+ if (acquireFenceChanged && s.acquireFence &&
+ s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ ATRACE_NAME("fence unsignaled");
ready = false;
}
sp<Layer> layer = nullptr;
if (s.surface) {
layer = fromHandleLocked(s.surface).promote();
- } else {
+ } else if (acquireFenceChanged) {
ALOGW("Transaction with buffer, but no Layer?");
continue;
}
@@ -3361,26 +3465,81 @@
continue;
}
- if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
- ATRACE_NAME("!isVsyncValidForUid");
- ready = false;
- }
- if (updateTransactionCounters) {
- // See BufferStateLayer::mPendingBufferTransactions
- layer->incrementPendingBufferCount();
- }
+ ATRACE_NAME(layer->getName().c_str());
- // If backpressure is enabled and we already have a buffer to commit, keep the transaction
- // in the queue.
- const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
- if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
- ready = false;
+ if (acquireFenceChanged) {
+ // If backpressure is enabled and we already have a buffer to commit, keep the
+ // transaction in the queue.
+ const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
+ if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
+ ready = false;
+ }
+ pendingBuffers.insert(s.surface);
}
- pendingBuffers.insert(s.surface);
}
return ready;
}
+void SurfaceFlinger::queueTransaction(TransactionState& state) {
+ Mutex::Autolock _l(mQueueLock);
+
+ // If its TransactionQueue already has a pending TransactionState or if it is pending
+ auto itr = mPendingTransactionQueues.find(state.applyToken);
+ // if this is an animation frame, wait until prior animation frame has
+ // been applied by SF
+ 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 = mPendingTransactionQueues.find(state.applyToken);
+ }
+ }
+
+ // 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));
+ }
+
+ mTransactionQueue.emplace(state);
+ ATRACE_INT("TransactionQueue", mTransactionQueue.size());
+
+ 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::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!");
+ }
+}
+
+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,
@@ -3389,6 +3548,13 @@
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
ATRACE_CALL();
+ // Check for incoming buffer updates and increment the pending buffer count.
+ for (const auto& state : states) {
+ if ((state.state.what & layer_state_t::eAcquireFenceChanged) && (state.state.surface)) {
+ mBufferCountTracker.increment(state.state.surface->localBinder());
+ }
+ }
+
uint32_t permissions =
callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
// Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
@@ -3398,107 +3564,30 @@
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};
+ queueTransaction(state);
- {
- Mutex::Autolock _l(mQueueLock);
- // If its TransactionQueue already has a pending TransactionState or if it is pending
- auto itr = mPendingTransactionQueues.find(applyToken);
- // if this is an animation frame, wait until prior animation frame has
- // been applied by SF
- if (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 = mPendingTransactionQueues.find(applyToken);
- }
- }
-
- const bool pendingTransactions = itr != mPendingTransactionQueues.end();
- // Expected present time is computed and cached on invalidate, so it may be stale.
- if (!pendingTransactions) {
- const auto now = systemTime();
- const bool nextVsyncPending = now < mExpectedPresentTime.load();
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
- mExpectedPresentTime = calculateExpectedPresentTime(stats);
- // The transaction might arrive just before the next vsync but after
- // invalidate was called. In that case we need to get the next vsync
- // afterwards.
- if (nextVsyncPending) {
- mExpectedPresentTime += stats.vsyncPeriod;
- }
- }
-
- mTransactionQueue.emplace(frameTimelineInfo, states, displays, flags, applyToken,
- inputWindowCommands, desiredPresentTime, isAutoTimestamp,
- uncacheBuffer, postTime, permissions, hasListenerCallbacks,
- listenerCallbacks, originPid, originUid, transactionId);
-
- if (pendingTransactions ||
- (!isAutoTimestamp && desiredPresentTime > mExpectedPresentTime.load())) {
- setTransactionFlags(eTransactionFlushNeeded);
- return NO_ERROR;
- }
-
- // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
- if (flags & eEarlyWakeup) {
- ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
- }
-
- if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
- (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
- flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
- }
-
- const auto schedule = [](uint32_t flags) {
- if (flags & eEarlyWakeup) return TransactionSchedule::Early;
- if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
- if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
- return TransactionSchedule::Late;
- }(flags);
- setTransactionFlags(eTransactionFlushNeeded, schedule);
- }
-
- // 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 NO_ERROR;
- }
-
- Mutex::Autolock _l(mStateLock);
- 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 (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;
- }
+ // Check the pending state to make sure the transaction is synchronous.
+ if (state.transactionCommittedSignal) {
+ waitForSynchronousTransaction(*state.transactionCommittedSignal);
}
return NO_ERROR;
@@ -3796,9 +3885,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;
@@ -3907,12 +3993,6 @@
flags |= eTraversalNeeded;
}
}
- FrameTimelineInfo info;
- if (what & layer_state_t::eFrameTimelineInfoChanged) {
- info = s.frameTimelineInfo;
- } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- info = frameTimelineInfo;
- }
if (what & layer_state_t::eFixedTransformHintChanged) {
if (layer->setFixedTransformHint(s.fixedTransformHint)) {
flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
@@ -3975,11 +4055,12 @@
: layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
- s.cachedBuffer, frameNumber, dequeueBufferTimestamp, info)) {
+ s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo,
+ s.releaseBufferListener)) {
flags |= eTraversalNeeded;
}
- } else if (info.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- layer->setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+ } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
@@ -4052,10 +4133,16 @@
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) {
@@ -4222,6 +4309,7 @@
auto it = mLayersByLocalBinderToken.begin();
while (it != mLayersByLocalBinderToken.end()) {
if (it->second == layer) {
+ mBufferCountTracker.remove(it->first->localBinder());
it = mLayersByLocalBinderToken.erase(it);
} else {
it++;
@@ -4387,6 +4475,7 @@
{"--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)},
@@ -4491,9 +4580,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);
@@ -4523,6 +4609,13 @@
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 {
result.append("Static screen stats:\n");
for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
@@ -4937,6 +5030,7 @@
case DESTROY_DISPLAY:
case ENABLE_VSYNC_INJECTIONS:
case GET_ANIMATION_FRAME_STATS:
+ case OVERRIDE_HDR_TYPES:
case GET_HDR_CAPABILITIES:
case SET_DESIRED_DISPLAY_MODE_SPECS:
case GET_DESIRED_DISPLAY_MODE_SPECS:
@@ -4952,12 +5046,12 @@
case GET_DISPLAYED_CONTENT_SAMPLE:
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
- case ADD_FPS_LISTENER:
- case REMOVE_FPS_LISTENER:
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",
@@ -5016,6 +5110,8 @@
// This is not sensitive information, so should not require permission control.
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
@@ -5872,6 +5968,15 @@
regionSampling, grayscale, captureResults);
});
+ // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
+ // Only do this when we're not doing region sampling, to allow the region sampling thread to
+ // manage buffer lifecycle itself.
+ if (!regionSampling &&
+ getRenderEngine().getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
+ getRenderEngine().unbindExternalTextureBuffer(buffer->getId());
+ }
+
captureResults.result = result;
captureListener->onScreenCaptureCompleted(captureResults);
}));
@@ -5991,8 +6096,12 @@
base::unique_fd bufferFence;
base::unique_fd drawFence;
getRenderEngine().useProtectedContext(useProtected);
+
+ // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
+ const bool useFramebufferCache = getRenderEngine().getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
- /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+ useFramebufferCache, std::move(bufferFence), &drawFence);
if (drawFence >= 0) {
sp<Fence> releaseFence = new Fence(dup(drawFence));
@@ -6010,10 +6119,7 @@
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
-
- mPendingSyncInputWindows = false;
-
- mTransactionCV.broadcast();
+ signalSynchronousTransactions();
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 68f22e8..e4ff6c9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -363,6 +363,7 @@
friend class BufferQueueLayer;
friend class BufferStateLayer;
friend class Client;
+ friend class FpsReporter;
friend class Layer;
friend class MonitoredProducer;
friend class RefreshRateOverlay;
@@ -416,6 +417,43 @@
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
+ // 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;
@@ -436,6 +474,41 @@
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,
@@ -479,6 +552,7 @@
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>
@@ -568,6 +642,8 @@
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
status_t clearAnimationFrameStats() override;
status_t getAnimationFrameStats(FrameStats* outStats) const override;
+ status_t overrideHdrTypes(const sp<IBinder>& displayToken,
+ const std::vector<ui::Hdr>& hdrTypes) override;
status_t enableVSyncInjections(bool enable) override;
status_t injectVSync(nsecs_t when) override;
status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
@@ -590,8 +666,7 @@
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 addFpsListener(const sp<IBinder>& layerHandle,
- const sp<gui::IFpsListener>& listener) 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,
@@ -607,7 +682,8 @@
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 setDisplayBrightness(const sp<IBinder>& displayToken,
+ const gui::DisplayBrightness& brightness) override;
status_t notifyPowerBoost(int32_t boostId) override;
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
@@ -763,13 +839,14 @@
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(
- bool isAutoTimestamp, int64_t desiredPresentTime, const Vector<ComposerState>& states,
- bool updateTransactionCounters,
+ const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
+ bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const;
/*
* Layer management
*/
@@ -906,7 +983,8 @@
/*
* Display management
*/
- DisplayModes loadSupportedDisplayModes(PhysicalDisplayId) const;
+ void loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+ DisplayModePtr& outActiveMode) const REQUIRES(mStateLock);
sp<DisplayDevice> setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -1017,10 +1095,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);
@@ -1040,6 +1115,11 @@
// 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
*/
@@ -1068,8 +1148,7 @@
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;
@@ -1171,9 +1250,6 @@
TransactionCallbackInvoker mTransactionCallbackInvoker;
- // Restrict layers to use two buffers in their bufferqueues.
- bool mLayerTripleBufferingDisabled = false;
-
// these are thread safe
std::unique_ptr<MessageQueue> mEventQueue;
FrameTracker mAnimFrameTracker;
@@ -1281,7 +1357,6 @@
sp<SetInputWindowsListener> mSetInputWindowsListener;
- bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
// This should only be accessed on the main thread.
@@ -1309,6 +1384,8 @@
int mFrameRateFlexibilityTokenCount = 0;
sp<IBinder> mDebugFrameRateFlexibilityToken;
+
+ BufferCountTracker mBufferCountTracker;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index c043866..b3dca78 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -380,5 +380,9 @@
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 816cb09..b19d216 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -100,6 +100,8 @@
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 b0413f1..8a3be9f 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -137,7 +137,7 @@
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);
addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
@@ -459,8 +459,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);
diff --git a/services/surfaceflinger/TimeStats/OWNERS b/services/surfaceflinger/TimeStats/OWNERS
index 1441f91..ded3ebb 100644
--- a/services/surfaceflinger/TimeStats/OWNERS
+++ b/services/surfaceflinger/TimeStats/OWNERS
@@ -1,2 +1 @@
alecmouri@google.com
-zzyiwei@google.com
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 100354a..974ae84 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -1070,6 +1070,9 @@
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");
@@ -1079,7 +1082,10 @@
ATRACE_CALL();
mTimeStatsTracker.clear();
- mTimeStats.stats.clear();
+
+ for (auto& globalRecord : mTimeStats.stats) {
+ globalRecord.second.stats.clear();
+ }
ALOGD("Cleared layer stats");
}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 4556bad..5ee28ce 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -127,6 +127,12 @@
Histogram displayDeadlineDeltas;
Histogram displayPresentDeltas;
std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
+
+ void clearGlobals() {
+ jankPayload = {};
+ displayDeadlineDeltas = {};
+ displayPresentDeltas = {};
+ }
};
class TimeStatsGlobal {
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/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index a78510e..3590e76 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -201,7 +201,8 @@
handle->dequeueReadyTime);
transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
handle->previousReleaseFence,
- handle->transformHint, eventStats, jankData);
+ handle->transformHint, eventStats, jankData,
+ handle->previousBufferId);
}
return NO_ERROR;
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index a240c82..caa8a4f 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -50,6 +50,7 @@
nsecs_t refreshStartTime = 0;
nsecs_t dequeueReadyTime = 0;
uint64_t frameNumber = 0;
+ uint64_t previousBufferId = 0;
};
class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 4d25a7a..ee5542d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -454,3 +454,12 @@
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 0e0be09..47e14f6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -45,6 +45,10 @@
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"
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 78187f7..b96725f 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -44,6 +44,7 @@
"MultiDisplayLayerBounds_test.cpp",
"RefreshRateOverlay_test.cpp",
"RelativeZ_test.cpp",
+ "ReleaseBufferCallback_test.cpp",
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
"SetGeometry_test.cpp",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 4868c12..03f8e1a 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);
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index fafb49e..f470eda 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -55,7 +55,7 @@
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);
});
@@ -76,7 +76,7 @@
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);
});
@@ -97,7 +97,7 @@
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,72 @@
}
}
+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.setFrame(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/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 6d28e62..158801a 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) {
@@ -750,7 +799,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 +823,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 +840,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 +868,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 +885,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 +912,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 +922,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 52e1a4d..7505e6e 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,
@@ -377,10 +380,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));
@@ -405,10 +405,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(
@@ -518,7 +515,7 @@
ISurfaceComposerClient::eFXSurfaceEffect));
Transaction()
- .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setCrop(colorLayer, Rect(0, 0, 32, 32))
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
@@ -557,7 +554,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;
@@ -568,7 +565,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));
@@ -730,7 +727,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();
@@ -744,7 +741,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();
@@ -759,7 +756,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;
@@ -786,7 +783,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);
@@ -943,7 +940,7 @@
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);
@@ -969,13 +966,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);
}
}
@@ -1004,7 +1001,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);
@@ -1015,10 +1012,7 @@
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);
@@ -1062,7 +1056,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);
@@ -1087,10 +1081,10 @@
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);
@@ -1102,8 +1096,8 @@
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();
+ // setCrop is applied immediately by default, with or without resize pending
+ Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
{
SCOPED_TRACE("resize pending");
auto shot = getScreenCapture();
@@ -1341,10 +1335,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++;
@@ -1377,10 +1368,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++;
@@ -1413,10 +1401,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++;
@@ -1499,10 +1484,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;
@@ -1528,10 +1510,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;
@@ -1549,10 +1528,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);
Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
@@ -1568,10 +1544,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;
@@ -1589,10 +1562,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;
@@ -1610,10 +1580,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);
Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
@@ -1629,7 +1596,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();
{
@@ -1686,8 +1653,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();
{
@@ -1747,8 +1714,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
index f010786..fa1a5ed 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -114,34 +114,5 @@
ASSERT_EQ(results.result, results2.result);
}
-/**
- * Parcel a layer_state_t struct, and then unparcel. Ensure that the object that was parceled
- * matches the object that's unparceled.
- */
-TEST(LayerStateTest, ParcelUnparcelLayerStateT) {
- layer_state_t input;
- input.frameTimelineInfo.vsyncId = 1;
- input.frameTimelineInfo.inputEventId = 2;
- Parcel p;
- input.write(p);
- layer_state_t output;
- p.setDataPosition(0);
- output.read(p);
- ASSERT_EQ(input.frameTimelineInfo.vsyncId, output.frameTimelineInfo.vsyncId);
- ASSERT_EQ(input.frameTimelineInfo.inputEventId, output.frameTimelineInfo.inputEventId);
-}
-
-TEST(LayerStateTest, LayerStateMerge_SelectsValidInputEvent) {
- layer_state_t layer1;
- layer1.frameTimelineInfo.inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID;
- layer_state_t layer2;
- layer2.frameTimelineInfo.inputEventId = 1;
- layer2.what |= layer_state_t::eFrameTimelineInfoChanged;
-
- layer1.merge(layer2);
-
- ASSERT_EQ(1, layer1.frameTimelineInfo.inputEventId);
-}
-
} // namespace test
} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index eba2c25..87c7b7d 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -21,6 +21,7 @@
#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>
@@ -138,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);
@@ -207,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);
@@ -245,6 +246,18 @@
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;
@@ -285,7 +298,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);
@@ -307,4 +320,4 @@
} // 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
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 782a364..ac5e297 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 =
@@ -143,7 +145,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));
@@ -197,7 +199,7 @@
if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
Transaction()
.setCornerRadius(layer, cornerRadius)
- .setCrop_legacy(layer, Rect(0, 0, size, size))
+ .setCrop(layer, Rect(0, 0, size, size))
.apply();
} else {
Transaction()
@@ -234,13 +236,13 @@
auto transaction = Transaction()
.setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .setCrop(parent, Rect(0, 0, size, size))
.reparent(child, parent)
.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));
+ transaction.setCrop(parent, Rect(0, 0, size, size));
} else {
transaction.setFrame(parent, Rect(0, 0, size, size));
}
@@ -276,7 +278,7 @@
if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
Transaction()
.setCornerRadius(parent, cornerRadius)
- .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .setCrop(parent, Rect(0, 0, size, size))
.reparent(child, parent)
.setPosition(child, 0, size / 2)
.apply();
@@ -301,47 +303,86 @@
}
}
-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)
+ .setFrame(leftLayer, {0, 0, canvasSize * 2, canvasSize * 2})
+ .setLayer(leftLayer, mLayerZBase + 1)
+ .setFrame(leftLayer, leftRect)
+ .setLayer(rightLayer, mLayerZBase + 2)
+ .setPosition(rightLayer, rightRect.left, rightRect.top)
+ .setFrame(rightLayer, rightRect)
+ .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)
+ .setFrame(blurLayer, blurRect)
+ .setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
+ .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;
@@ -378,25 +419,15 @@
}
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurAffectedByParentAlpha) {
- 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;
- }
-
- property_get("debug.renderengine.backend", value, "");
- if (strcmp(value, "skiagl") != 0) {
- // This device isn't using Skia render engine, no-op.
- return;
- }
+ if (!deviceSupportsBlurs()) GTEST_SKIP();
+ if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
sp<SurfaceControl> left;
sp<SurfaceControl> right;
sp<SurfaceControl> blur;
sp<SurfaceControl> blurParent;
- const auto size = 32;
+ 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));
@@ -485,7 +516,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();
@@ -493,10 +524,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));
@@ -512,10 +540,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 f8a0bc1..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;
@@ -84,8 +84,8 @@
.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, -1).setLayer(childLayer, 1).apply();
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ec826ae..e4a1f66 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -131,7 +131,7 @@
asTransaction([&](Transaction& t) {
t.setSize(mFGSurfaceControl, 64, 64);
t.setPosition(mFGSurfaceControl, 64, 64);
- t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+ t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
});
EXPECT_INITIAL_STATE("After restoring initial state");
@@ -225,7 +225,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);
@@ -234,9 +234,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);
@@ -351,7 +349,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));
});
{
@@ -947,7 +945,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);
@@ -1007,7 +1005,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);
});
@@ -1043,7 +1041,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});
@@ -1074,7 +1072,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);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 16826c1..613b21e 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();
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index eaf54e3..08de01c 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -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,
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
index 4598f9d..b0753c8 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -328,7 +328,7 @@
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);
+ SurfaceComposerClient::Transaction().setCrop(child, layerCrop).show(child).apply(true);
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = child->getHandle();
@@ -623,7 +623,7 @@
.setLayerStack(layer, 0)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
- .setCrop_legacy(layer, bounds)
+ .setCrop(layer, bounds)
.apply();
DisplayCaptureArgs captureArgs;
@@ -671,8 +671,8 @@
.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_legacy(layer, bounds)
- .setCrop_legacy(childLayer, childBounds)
+ .setCrop(layer, bounds)
+ .setCrop(childLayer, childBounds)
.apply();
DisplayCaptureArgs captureArgs;
@@ -853,9 +853,7 @@
}
TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
- SurfaceComposerClient::Transaction()
- .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
- .apply(true);
+ SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
// Even though the parent is cropped out we should still capture the child.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a20d5c6..d9cab42 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -370,7 +370,7 @@
}
void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
- t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
+ t.setCrop(mBGSurfaceControl, CROP_UPDATE);
}
void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 11bd9eb..820f248 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1324,7 +1324,7 @@
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
- ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+ ts.setCrop(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
@@ -1692,7 +1692,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
@@ -1915,7 +1915,7 @@
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();
@@ -2010,7 +2010,7 @@
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));
+ ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
}
void Test_SurfacePositionLatching() {
@@ -2043,7 +2043,7 @@
{
TransactionScope ts(*Base::sFakeComposer);
ts.setSize(Base::mFGSurfaceControl, 128, 128);
- ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+ ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
}
auto referenceFrame1 = Base::mBaseFrame;
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6bc2318..6a7ec9b 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -31,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)};
@@ -41,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);
}
@@ -50,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);
}
@@ -77,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);
}
@@ -90,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++;
}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 256be27..4e1c0c7 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -836,8 +836,8 @@
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 */);
@@ -884,7 +884,7 @@
});
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;
}
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index a9e5df3..a2291b2 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -76,7 +76,7 @@
void setupScheduler();
void setupComposer(uint32_t virtualDisplayCount);
- sp<BufferStateLayer> createBufferStateLayer();
+ sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
TestableSurfaceFlinger mFlinger;
Hwc2::mock::Composer* mComposer = nullptr;
@@ -91,7 +91,7 @@
sp<Layer> mUnrelated;
sp<TestableFpsListener> mFpsListener;
- sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline);
+ sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger()));
};
FpsReporterTest::FpsReporterTest() {
@@ -110,10 +110,10 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer() {
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
sp<Client> client;
LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
+ LAYER_FLAGS, metadata);
return new BufferStateLayer(args);
}
@@ -154,7 +154,10 @@
TEST_F(FpsReporterTest, callsListeners) {
mParent = createBufferStateLayer();
- mTarget = createBufferStateLayer();
+ const constexpr int32_t kTaskId = 12;
+ LayerMetadata targetMetadata;
+ targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+ mTarget = createBufferStateLayer(targetMetadata);
mChild = createBufferStateLayer();
mGrandChild = createBufferStateLayer();
mUnrelated = createBufferStateLayer();
@@ -162,6 +165,10 @@
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;
@@ -170,7 +177,7 @@
mGrandChild->getSequence())))
.WillOnce(Return(expectedFps));
- mFpsReporter->addListener(mFpsListener, mTarget);
+ mFpsReporter->addListener(mFpsListener, kTaskId);
mFpsReporter->dispatchLayerFps();
EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
mFpsReporter->removeListener(mFpsListener);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 9a4e020..d1385c0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -40,6 +40,7 @@
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 {
@@ -67,7 +68,7 @@
void SetUp() override {
mTimeStats = std::make_shared<mock::TimeStats>();
mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
- kTestThresholds);
+ kTestThresholds, kHwcDuration);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
@@ -162,6 +163,7 @@
static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
kDeadlineThreshold,
kStartThreshold};
+ static constexpr nsecs_t kHwcDuration = std::chrono::nanoseconds(3ns).count();
};
static const std::string sLayerNameOne = "layer1";
@@ -191,10 +193,12 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
@@ -202,7 +206,8 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -211,7 +216,8 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -220,7 +226,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -231,7 +238,8 @@
constexpr int32_t inputEventId = 1;
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -241,7 +249,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -269,11 +278,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdTwo, sLayerNameTwo,
- sLayerNameTwo);
+ sLayerNameTwo, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -315,7 +324,8 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -337,7 +347,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -351,18 +361,20 @@
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ 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");
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -376,7 +388,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -392,7 +405,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -408,7 +422,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -418,39 +433,78 @@
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(11);
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ // Deadline delta is 2ms because, sf's adjusted deadline is 60 - composerTime(3) = 57ms.
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
sLayerNameOne,
- JankType::SurfaceFlingerCpuDeadlineMissed,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(3ms)
- .count(),
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(10ms)
- .count(),
+ JankType::SurfaceFlingerCpuDeadlineMissed, 2, 10,
0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ 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);
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(70ms).count());
+ presentFence1->signalForTest(70);
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(59ms).count(), presentFence1);
+ mFrameTimeline->setSfPresent(59, presentFence1);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
@@ -458,190 +512,194 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
sLayerNameOne, JankType::DisplayHAL,
- 0, 0, 0}));
+ -1, 0, 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ 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);
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
+ surfaceFrame1->setAcquireFenceTime(20);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1);
+ 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, 0, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(25ms)
- .count()}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::AppDeadlineMissed, -1, 0,
+ 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(),
- std::chrono::nanoseconds(90ms).count()});
+ 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);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(45);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) {
- Fps refreshRate = Fps::fromPeriodNsecs(std::chrono::nanoseconds(32ms).count());
+ Fps refreshRate = Fps::fromPeriodNsecs(32);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
- JankType::SurfaceFlingerScheduling, 0, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(-10ms)
- .count()}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::SurfaceFlingerScheduling,
+ -1, 0, -10}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(40ms).count(), std::chrono::nanoseconds(60ms).count(),
- std::chrono::nanoseconds(92ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ 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);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(50ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(60ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1);
+ presentFence1->signalForTest(60);
+ mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) {
- Fps refreshRate = Fps(16.66f);
+ Fps refreshRate = Fps::fromPeriodNsecs(16);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne, JankType::PredictionError, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(5ms)
- .count(),
- 0}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::PredictionError, -1, 5,
+ 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(30ms).count(), std::chrono::nanoseconds(40ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ 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);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(65ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1);
+ presentFence1->signalForTest(65);
+ mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) {
- Fps refreshRate = Fps::fromPeriodNsecs(std::chrono::nanoseconds(32ms).count());
+ Fps refreshRate = Fps::fromPeriodNsecs(32);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
- JankType::BufferStuffing |
- JankType::SurfaceFlingerScheduling,
- 0, 0, 0}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::BufferStuffing, -1, 0,
+ 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(30ms).count(), std::chrono::nanoseconds(40ms).count(),
- std::chrono::nanoseconds(58ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(),
- std::chrono::nanoseconds(90ms).count()});
+ 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);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(82ms).count(), refreshRate);
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented,
- /*previousLatchTime*/
- std::chrono::nanoseconds(56ms).count());
+ /*previousLatchTime*/ 56);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
- EXPECT_EQ(surfaceFrame1->getJankType(),
- JankType::BufferStuffing | JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
- Fps refreshRate = Fps(11.0);
- Fps renderRate = Fps(30.0);
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps renderRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
- JankType::AppDeadlineMissed, 0, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(25ms)
- .count()}));
+ JankType::AppDeadlineMissed, -1, 0, 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(),
- std::chrono::nanoseconds(90ms).count()});
+ 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);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sLayerNameOne, /*isBuffer*/ true);
+ surfaceFrame1->setAcquireFenceTime(45);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setRenderRate(renderRate);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1);
+ 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,
+ -1, -1, 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);
+}
+
/*
* Tracing Tests
*
@@ -656,7 +714,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -682,7 +741,8 @@
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -727,7 +787,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -757,7 +818,7 @@
ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart(
int64_t cookie, int64_t token, pid_t pid, ProtoPresentType presentType, bool onTimeFinish,
- bool gpuComposition, ProtoJankType jankType) {
+ bool gpuComposition, ProtoJankType jankType, ProtoPredictionType predictionType) {
ProtoActualDisplayFrameStart proto;
proto.set_cookie(cookie);
proto.set_token(token);
@@ -766,6 +827,7 @@
proto.set_on_time_finish(onTimeFinish);
proto.set_gpu_composition(gpuComposition);
proto.set_jank_type(jankType);
+ proto.set_prediction_type(predictionType);
return proto;
}
@@ -785,7 +847,7 @@
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) {
+ ProtoJankType jankType, ProtoPredictionType predictionType) {
ProtoActualSurfaceFrameStart proto;
proto.set_cookie(cookie);
proto.set_token(token);
@@ -796,6 +858,7 @@
proto.set_on_time_finish(onTimeFinish);
proto.set_gpu_composition(gpuComposition);
proto.set_jank_type(jankType);
+ proto.set_prediction_type(predictionType);
return proto;
}
@@ -836,6 +899,8 @@
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,
@@ -881,6 +946,8 @@
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) {
@@ -893,7 +960,7 @@
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
- int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
+ int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
@@ -909,7 +976,8 @@
createProtoActualDisplayFrameStart(traceCookie + 2, displayFrameToken1,
kSurfaceFlingerPid,
FrameTimelineEvent::PRESENT_ON_TIME, true, false,
- FrameTimelineEvent::JANK_NONE);
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 2);
addEmptyDisplayFrame();
@@ -933,7 +1001,7 @@
// Packet - 1 : FrameEnd (ExpectedDisplayFrame)
const auto& packet1 = packets[1];
ASSERT_TRUE(packet1.has_timestamp());
- EXPECT_EQ(packet1.timestamp(), 25u);
+ EXPECT_EQ(packet1.timestamp(), 30u);
ASSERT_TRUE(packet1.has_frame_timeline_event());
const auto& event1 = packet1.frame_timeline_event();
@@ -955,7 +1023,7 @@
// Packet - 3 : FrameEnd (ActualDisplayFrame)
const auto& packet3 = packets[3];
ASSERT_TRUE(packet3.has_timestamp());
- EXPECT_EQ(packet3.timestamp(), 26u);
+ EXPECT_EQ(packet3.timestamp(), 31u);
ASSERT_TRUE(packet3.has_frame_timeline_event());
const auto& event3 = packet3.frame_timeline_event();
@@ -984,7 +1052,8 @@
createProtoActualDisplayFrameStart(traceCookie + 1, displayFrameToken1,
kSurfaceFlingerPid,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
- false, FrameTimelineEvent::JANK_UNKNOWN);
+ false, FrameTimelineEvent::JANK_UNKNOWN,
+ FrameTimelineEvent::PREDICTION_EXPIRED);
auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1);
addEmptyDisplayFrame();
@@ -1009,7 +1078,7 @@
// Packet - 1 : FrameEnd (ActualDisplayFrame)
const auto& packet1 = packets[1];
ASSERT_TRUE(packet1.has_timestamp());
- EXPECT_EQ(packet1.timestamp(), 26u);
+ EXPECT_EQ(packet1.timestamp(), 31u);
ASSERT_TRUE(packet1.has_frame_timeline_event());
const auto& event1 = packet1.frame_timeline_event();
@@ -1032,11 +1101,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1054,7 +1123,8 @@
createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken,
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
- FrameTimelineEvent::JANK_NONE);
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
auto protoPresentedSurfaceFrameExpectedStart =
@@ -1065,7 +1135,8 @@
createProtoActualSurfaceFrameStart(traceCookie + 4, surfaceFrameToken,
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_ON_TIME, true, false,
- FrameTimelineEvent::JANK_NONE);
+ FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
// Set up the display frame
@@ -1179,12 +1250,9 @@
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
- constexpr nsecs_t appStartTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count();
- constexpr nsecs_t appEndTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count();
- constexpr nsecs_t appPresentTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count();
+ 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});
@@ -1193,16 +1261,13 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
- constexpr nsecs_t sfStartTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count();
- constexpr nsecs_t sfEndTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count();
- constexpr nsecs_t sfPresentTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count();
+ 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});
@@ -1213,7 +1278,8 @@
createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
- false, FrameTimelineEvent::JANK_UNKNOWN);
+ false, FrameTimelineEvent::JANK_UNKNOWN,
+ FrameTimelineEvent::PREDICTION_EXPIRED);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
// Set up the display frame
@@ -1261,11 +1327,11 @@
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, 26, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1292,8 +1358,8 @@
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
Fps vsyncRate = Fps::fromPeriodNsecs(11);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ 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);
@@ -1331,8 +1397,8 @@
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
Fps vsyncRate = Fps::fromPeriodNsecs(11);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ 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);
@@ -1411,22 +1477,44 @@
EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
}
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(52);
+
+ // 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, 52);
+ EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ 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);
+ 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);
+ mFrameTimeline->setSfPresent(27, presentFence1);
auto displayFrame1 = getDisplayFrame(0);
auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
presentFence1->signalForTest(30);
@@ -1441,12 +1529,12 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
- mFrameTimeline->setSfPresent(56, presentFence2);
+ mFrameTimeline->setSfPresent(57, presentFence2);
auto displayFrame2 = getDisplayFrame(1);
auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
@@ -1494,14 +1582,14 @@
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ 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);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1521,12 +1609,12 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
- mFrameTimeline->setSfPresent(56, presentFence2);
+ mFrameTimeline->setSfPresent(57, presentFence2);
auto displayFrame2 = getDisplayFrame(1);
auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
@@ -1575,12 +1663,12 @@
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ 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);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1617,14 +1705,14 @@
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ 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);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1644,7 +1732,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1693,12 +1781,12 @@
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 56, 60});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
+ 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);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1718,7 +1806,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1764,16 +1852,95 @@
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::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1784,11 +1951,12 @@
}
TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) {
- const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
- const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ 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);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1797,7 +1965,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1808,11 +1977,12 @@
}
TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) {
- const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
- const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ 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);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1821,7 +1991,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1832,11 +2003,12 @@
}
TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) {
- const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
- const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ 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);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1845,7 +2017,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1856,14 +2029,15 @@
}
TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) {
- const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
- const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
- const auto threeHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(300ms).count();
- const auto fiveHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
- const auto sixHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(600ms).count();
+ 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);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1872,7 +2046,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1881,7 +2056,8 @@
auto surfaceFrame3 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame3);
@@ -1890,7 +2066,8 @@
auto surfaceFrame4 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame4);
@@ -1899,7 +2076,8 @@
auto surfaceFrame5 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ 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);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e46a270..38e503f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -64,7 +64,7 @@
}
} mExpectDisableVsync{mSchedulerCallback};
- TestableScheduler mScheduler{mConfigs, mSchedulerCallback};
+ TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
@@ -85,8 +85,10 @@
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
+ mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
+
+ mFlinger.resetScheduler(mScheduler);
}
} // namespace
@@ -94,85 +96,84 @@
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- const sp<IDisplayEventConnection> connection = mScheduler.createDisplayEventConnection(handle);
+ const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler.getEventConnection(handle));
+ 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);
- mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
+ mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- mScheduler.onScreenAcquired(handle);
+ mScheduler->onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- mScheduler.onScreenReleased(handle);
+ mScheduler->onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- mScheduler.dump(handle, output);
+ mScheduler->dump(handle, output);
EXPECT_TRUE(output.empty());
EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
- mScheduler.setDuration(handle, 10ns, 20ns);
+ mScheduler->setDuration(handle, 10ns, 20ns);
}
TEST_F(SchedulerTest, validConnectionHandle) {
const sp<IDisplayEventConnection> connection =
- mScheduler.createDisplayEventConnection(mConnectionHandle);
+ mScheduler->createDisplayEventConnection(mConnectionHandle);
ASSERT_EQ(mEventThreadConnection, connection);
- EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
+ EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
+ mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- mScheduler.onScreenAcquired(mConnectionHandle);
+ mScheduler->onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- mScheduler.onScreenReleased(mConnectionHandle);
+ mScheduler->onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- mScheduler.dump(mConnectionHandle, output);
+ mScheduler->dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
- mScheduler.setDuration(mConnectionHandle, 10ns, 20ns);
+ mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
static constexpr size_t kEventConnections = 5;
EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
+ EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
TEST_F(SchedulerTest, noLayerHistory) {
// Layer history should not be created if there is a single config.
- ASSERT_FALSE(mScheduler.hasLayerHistory());
+ ASSERT_FALSE(mScheduler->hasLayerHistory());
- TestableSurfaceFlinger flinger;
- mock::MockLayer layer(flinger.flinger());
+ sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
// Content detection should be no-op.
- mScheduler.registerLayer(&layer);
- mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->registerLayer(layer.get());
+ mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
constexpr bool kPowerStateNormal = true;
- mScheduler.setDisplayPowerState(kPowerStateNormal);
+ mScheduler->setDisplayPowerState(kPowerStateNormal);
constexpr uint32_t kDisplayArea = 999'999;
- mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+ mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
- mScheduler.chooseRefreshRateForContent();
+ 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());
+ mScheduler->clearOptionalFieldsInFeatures();
+ EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
}
@@ -183,9 +184,9 @@
// 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_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+ PHYSICAL_DISPLAY_ID, modeId,
+ vsyncPeriod));
EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
}
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index e903664..a72ab42 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -48,6 +48,7 @@
using testing::Contains;
using testing::HasSubstr;
using testing::InSequence;
+using testing::Not;
using testing::SizeIs;
using testing::StrEq;
using testing::UnorderedElementsAre;
@@ -855,6 +856,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) {
@@ -1074,6 +1093,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(8);
+ 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(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
@@ -1205,6 +1264,31 @@
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) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index eb2c1ba..7c431a0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -127,7 +127,6 @@
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -163,8 +162,6 @@
// 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(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .WillOnce(Return(time + nsecs_t(5 * 1e8)));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
/*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
@@ -177,7 +174,11 @@
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(1u, transactionQueue.size());
@@ -187,9 +188,11 @@
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
nsecs_t time = systemTime();
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .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,
@@ -229,7 +232,8 @@
// 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));
@@ -238,18 +242,9 @@
// transaction that would goes to pending transaciton queue.
mFlinger.flushTransactionQueues();
- // check that there is one binder on the pending queue.
+ // check that the transaction was applied.
auto transactionQueue = mFlinger.getPendingTransactionQueue();
- EXPECT_EQ(1u, transactionQueue.size());
-
- auto& [applyToken, transactionStates] = *(transactionQueue.begin());
- EXPECT_EQ(2u, transactionStates.size());
-
- auto& transactionStateA = transactionStates.front();
- transactionStates.pop();
- checkEqual(transactionA, transactionStateA);
- auto& transactionStateB = transactionStates.front();
- checkEqual(transactionB, transactionStateB);
+ EXPECT_EQ(0u, transactionQueue.size());
}
bool mHasListenerCallbacks = false;
@@ -262,10 +257,6 @@
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- // nsecs_t time = systemTime();
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .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), false, FrameTimelineInfo{});
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index dbadf75..b5ef0a1 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -123,7 +123,8 @@
traceTimestamp(layerId, bufferId, frameNumber, postTime,
FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
- frameNumber, dequeueTime, FrameTimelineInfo{});
+ frameNumber, dequeueTime, FrameTimelineInfo{},
+ nullptr /* releaseBufferCallback */);
commitTransaction(layer.get());
bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index bf9ec39..c75538f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -116,6 +116,7 @@
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());
}
@@ -125,7 +126,7 @@
auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -139,6 +140,7 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
}
@@ -149,7 +151,7 @@
auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*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;
@@ -159,7 +161,7 @@
sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
nsecs_t start = systemTime();
layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -172,12 +174,14 @@
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());
}
@@ -195,7 +199,7 @@
sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -204,6 +208,7 @@
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());
@@ -220,7 +225,7 @@
sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -250,7 +255,7 @@
sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 3, /*inputEventId*/ 0});
+ {/*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;
@@ -260,12 +265,15 @@
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());
@@ -297,10 +305,12 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(10, bufferlessSurfaceFrame1->getActuals().endTime);
EXPECT_EQ(2, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
}
@@ -327,9 +337,11 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Unknown, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(1, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
}
@@ -341,7 +353,7 @@
auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -349,7 +361,7 @@
auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -368,6 +380,129 @@
EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
}
+
+ void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ sp<Fence> fence1(new Fence());
+ auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
+ sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ 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);
+ sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ 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);
+ sp<GraphicBuffer> buffer3{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ 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());
+ sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ 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) {
@@ -407,4 +542,14 @@
TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) {
PendingSurfaceFramesRemovedAfterClassification();
}
+
+TEST_F(TransactionSurfaceFrameTest,
+ BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer) {
+ BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
+}
+
+TEST_F(TransactionSurfaceFrameTest, MultipleCommitsBeforeLatch) {
+ MultipleCommitsBeforeLatch();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index 106da81..17648d5 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -101,24 +101,6 @@
CHECK_REFRESH(1, kLate, kLate);
}
-TEST_F(VsyncModulatorTest, EarlyStartWithEarly) {
- 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::Early));
-
- 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));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index b9d1794..5707978 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -32,7 +32,7 @@
MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
- MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD3(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&, bool));
MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
};
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 2fefa45..ddaa5a1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -85,6 +85,7 @@
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);
}
@@ -159,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); }
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4f89353..2002bdf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -12,6 +12,15 @@
// 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",
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index bcd9957..c1795f5 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -87,45 +87,6 @@
// -------------------------------------------------------------------------------------------------
-static constexpr int MAX_RETRIES = 1;
-
-template <typename T>
-HalResult<T> HalController::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> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
- std::shared_ptr<HalWrapper> 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 Vibrator 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;
-}
-
-// -------------------------------------------------------------------------------------------------
-
bool HalController::init() {
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
@@ -134,11 +95,6 @@
return mConnectedHal != nullptr;
}
-HalResult<void> HalController::ping() {
- hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
- return apply(pingFn, "ping");
-}
-
void HalController::tryReconnect() {
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
@@ -148,84 +104,6 @@
}
}
-HalResult<void> HalController::on(milliseconds timeout,
- const std::function<void()>& completionCallback) {
- hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->on(timeout, completionCallback);
- };
- return apply(onFn, "on");
-}
-
-HalResult<void> HalController::off() {
- hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
- return apply(offFn, "off");
-}
-
-HalResult<void> HalController::setAmplitude(int32_t amplitude) {
- hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->setAmplitude(amplitude);
- };
- return apply(setAmplitudeFn, "setAmplitude");
-}
-
-HalResult<void> HalController::setExternalControl(bool enabled) {
- hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->setExternalControl(enabled);
- };
- return apply(setExternalControlFn, "setExternalControl");
-}
-
-HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
- hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->alwaysOnEnable(id, effect, strength);
- };
- return apply(alwaysOnEnableFn, "alwaysOnEnable");
-}
-
-HalResult<void> HalController::alwaysOnDisable(int32_t id) {
- hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->alwaysOnDisable(id);
- };
- return apply(alwaysOnDisableFn, "alwaysOnDisable");
-}
-
-HalResult<Capabilities> HalController::getCapabilities() {
- hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
- return hal->getCapabilities();
- };
- return apply(getCapabilitiesFn, "getCapabilities");
-}
-
-HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
- hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
- return hal->getSupportedEffects();
- };
- return apply(getSupportedEffectsFn, "getSupportedEffects");
-}
-
-HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() {
- hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn =
- [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); };
- return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
-}
-
-HalResult<milliseconds> HalController::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->performEffect(effect, strength, completionCallback);
- };
- return apply(performEffectFn, "performEffect");
-}
-
-HalResult<void> HalController::performComposedEffect(
- const std::vector<CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) {
- hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
- return hal->performComposedEffect(primitiveEffects, completionCallback);
- };
- return apply(performComposedEffectFn, "performComposedEffect");
-}
-
}; // namespace vibrator
}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 7fee82f..1010aa5 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -19,16 +19,19 @@
#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;
@@ -45,20 +48,6 @@
// -------------------------------------------------------------------------------------------------
template <class T>
-HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
- if (cache.has_value()) {
- // Return copy of cached value.
- return HalResult<T>::ok(*cache);
- }
- HalResult<T> ret = loadFn();
- if (ret.isOk()) {
- // Cache copy of returned value.
- cache.emplace(ret.value());
- }
- return ret;
-}
-
-template <class T>
bool isStaticCastValid(Effect effect) {
T castEffect = static_cast<T>(effect);
auto iter = hardware::hidl_enum_range<T>();
@@ -133,6 +122,117 @@
// -------------------------------------------------------------------------------------------------
+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());
}
@@ -168,9 +268,8 @@
return HalResult<void>::fromStatus(getHal()->off());
}
-HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
- float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
- return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude));
+HalResult<void> AidlHalWrapper::setAmplitude(float amplitude) {
+ return HalResult<void>::fromStatus(getHal()->setAmplitude(amplitude));
}
HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
@@ -185,26 +284,6 @@
return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
}
-HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
- std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
- return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
- mCapabilities);
-}
-
-HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
- std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
- return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
- this),
- mSupportedEffects);
-}
-
-HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() {
- std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
- return loadCached<std::vector<
- CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this),
- mSupportedPrimitives);
-}
-
HalResult<milliseconds> AidlHalWrapper::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
HalResult<Capabilities> capabilities = getCapabilities();
@@ -224,12 +303,33 @@
return ret;
}
-HalResult<void> AidlHalWrapper::performComposedEffect(
- const std::vector<CompositeEffect>& primitiveEffects,
+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);
- return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb));
+
+ 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() {
@@ -244,12 +344,72 @@
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;
@@ -290,8 +450,9 @@
}
template <typename I>
-HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) {
- auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude));
+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));
}
@@ -314,32 +475,6 @@
}
template <typename I>
-HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
- std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
- return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
- mCapabilities);
-}
-
-template <typename I>
-HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
- ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
- return HalResult<std::vector<Effect>>::unsupported();
-}
-
-template <typename I>
-HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() {
- ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available");
- return HalResult<std::vector<CompositePrimitive>>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
- const std::function<void()>&) {
- ALOGV("Skipped composed effect 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 =
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 8a08e5b..a9d499d 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -30,7 +30,8 @@
const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
HalResult<void> LegacyManagerHalWrapper::ping() {
- return mController->ping();
+ auto pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+ return mController->doWithRetry<void>(pingFn, "ping");
}
void LegacyManagerHalWrapper::tryReconnect() {
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 7b4cc19..a468146 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -12,6 +12,15 @@
// 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: [
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 0acff06..53f3daf 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -20,6 +20,10 @@
#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;
@@ -33,7 +37,7 @@
public:
void SetUp(State& /*state*/) override { mController.init(); }
- void TearDown(State& /*state*/) override { mController.off(); }
+ void TearDown(State& state) override { turnVibratorOff(state); }
static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
@@ -46,8 +50,8 @@
auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
- bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result,
- vibrator::Capabilities&& query, State& state) {
+ bool hasCapabilities(vibrator::Capabilities&& query, State& state) {
+ auto result = mController.getInfo().capabilities;
if (result.isFailed()) {
state.SkipWithError(result.errorMessage());
return false;
@@ -58,6 +62,10 @@
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()) {
@@ -66,6 +74,12 @@
}
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) \
@@ -93,7 +107,7 @@
BENCHMARK_WRAPPER(VibratorBench, ping, {
for (auto _ : state) {
state.ResumeTiming();
- auto ret = mController.ping();
+ auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); });
state.PauseTiming();
checkHalResult(ret, state);
}
@@ -111,10 +125,11 @@
for (auto _ : state) {
state.ResumeTiming();
- auto ret = mController.on(duration, callback);
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
state.PauseTiming();
if (checkHalResult(ret, state)) {
- checkHalResult(mController.off(), state);
+ turnVibratorOff(state);
}
}
});
@@ -125,65 +140,66 @@
for (auto _ : state) {
state.PauseTiming();
- if (!checkHalResult(mController.on(duration, callback), state)) {
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ if (!checkHalResult(ret, state)) {
continue;
}
state.ResumeTiming();
- checkHalResult(mController.off(), state);
+ turnVibratorOff(state);
}
});
BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
return;
}
auto duration = 60s;
auto callback = []() {};
- auto amplitude = UINT8_MAX;
+ auto amplitude = 1.0f;
for (auto _ : state) {
state.PauseTiming();
vibrator::HalController controller;
controller.init();
- if (!checkHalResult(controller.on(duration, callback), state)) {
+ auto result =
+ halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); });
+ if (!checkHalResult(result, state)) {
continue;
}
state.ResumeTiming();
- auto ret = controller.setAmplitude(amplitude);
+ auto ret =
+ halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); });
state.PauseTiming();
if (checkHalResult(ret, state)) {
- checkHalResult(controller.off(), state);
+ turnVibratorOff(state);
}
}
});
BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
return;
}
auto duration = 6000s;
auto callback = []() {};
- auto amplitude = UINT8_MAX;
+ auto amplitude = 1.0f;
- checkHalResult(mController.on(duration, callback), state);
+ auto onResult =
+ halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ checkHalResult(onResult, state);
for (auto _ : state) {
- checkHalResult(mController.setAmplitude(amplitude), state);
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+ checkHalResult(ret, state);
}
-
- checkHalResult(mController.off(), state);
});
BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
return;
}
@@ -192,103 +208,85 @@
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- auto ret = controller.setExternalControl(true);
+ auto ret =
+ halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); });
state.PauseTiming();
if (checkHalResult(ret, state)) {
- checkHalResult(controller.setExternalControl(false), state);
+ auto result = halCall<void>(controller,
+ [](auto hal) { return hal->setExternalControl(false); });
+ checkHalResult(result, state);
}
}
});
BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
return;
}
for (auto _ : state) {
state.ResumeTiming();
- auto ret = mController.setExternalControl(true);
+ auto result =
+ halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- checkHalResult(mController.setExternalControl(false), state);
+ if (checkHalResult(result, state)) {
+ auto ret = halCall<void>(mController,
+ [](auto hal) { return hal->setExternalControl(false); });
+ checkHalResult(ret, state);
}
}
});
BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
return;
}
- auto amplitude = UINT8_MAX;
+ auto amplitude = 1.0f;
- checkHalResult(mController.setExternalControl(true), state);
+ auto onResult =
+ halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
+ checkHalResult(onResult, state);
for (auto _ : state) {
- checkHalResult(mController.setAmplitude(amplitude), state);
+ auto ret =
+ halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
+ checkHalResult(ret, state);
}
- checkHalResult(mController.setExternalControl(false), state);
+ auto offResult =
+ halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); });
+ checkHalResult(offResult, state);
});
-BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+BENCHMARK_WRAPPER(VibratorBench, getInfo, {
for (auto _ : state) {
state.PauseTiming();
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- checkHalResult(controller.getCapabilities(), state);
+ 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, getCapabilitiesCached, {
+BENCHMARK_WRAPPER(VibratorBench, getInfoCached, {
// First call to cache values.
- checkHalResult(mController.getCapabilities(), state);
+ mController.getInfo();
for (auto _ : state) {
- checkHalResult(mController.getCapabilities(), state);
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
- for (auto _ : state) {
- state.PauseTiming();
- vibrator::HalController controller;
- controller.init();
- state.ResumeTiming();
- checkHalResult(controller.getSupportedEffects(), state);
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
- // First call to cache values.
- checkHalResult(mController.getSupportedEffects(), state);
-
- for (auto _ : state) {
- checkHalResult(mController.getSupportedEffects(), state);
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
- for (auto _ : state) {
- state.PauseTiming();
- vibrator::HalController controller;
- controller.init();
- state.ResumeTiming();
- checkHalResult(controller.getSupportedPrimitives(), state);
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
- // First call to cache values.
- checkHalResult(mController.getSupportedPrimitives(), state);
-
- for (auto _ : state) {
- checkHalResult(mController.getSupportedPrimitives(), 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);
}
});
@@ -296,12 +294,12 @@
public:
static void DefaultArgs(Benchmark* b) {
vibrator::HalController controller;
- auto effectsResult = controller.getSupportedEffects();
+ auto effectsResult = controller.getInfo().supportedEffects;
if (!effectsResult.isOk()) {
return;
}
- std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+ std::vector<Effect> supported = effectsResult.value();
b->ArgNames({"Effect", "Strength"});
if (supported.empty()) {
@@ -309,11 +307,11 @@
return;
}
- for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
+ for (const auto& effect : enum_range<Effect>()) {
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
continue;
}
- for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) {
+ for (const auto& strength : enum_range<EffectStrength>()) {
b->Args({static_cast<long>(effect), static_cast<long>(strength)});
}
}
@@ -323,18 +321,16 @@
bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
auto getEffect(const State& state) const {
- return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
+ return static_cast<Effect>(this->getOtherArg(state, 0));
}
auto getStrength(const State& state) const {
- return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1));
+ return static_cast<EffectStrength>(this->getOtherArg(state, 1));
}
};
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
if (!hasArgs(state)) {
@@ -347,18 +343,20 @@
for (auto _ : state) {
state.ResumeTiming();
- auto ret = mController.alwaysOnEnable(id, effect, strength);
+ auto ret = halCall<void>(mController, [&](auto hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ });
state.PauseTiming();
if (checkHalResult(ret, state)) {
- checkHalResult(mController.alwaysOnDisable(id), state);
+ auto disableResult =
+ halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+ checkHalResult(disableResult, state);
}
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
if (!hasArgs(state)) {
@@ -371,11 +369,16 @@
for (auto _ : state) {
state.PauseTiming();
- if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) {
+ auto enableResult = halCall<void>(mController, [&](auto hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ });
+ if (!checkHalResult(enableResult, state)) {
continue;
}
state.ResumeTiming();
- checkHalResult(mController.alwaysOnDisable(id), state);
+ auto disableResult =
+ halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
+ checkHalResult(disableResult, state);
}
});
@@ -390,10 +393,12 @@
for (auto _ : state) {
state.ResumeTiming();
- auto ret = mController.performEffect(effect, strength, callback);
+ auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+ return hal->performEffect(effect, strength, callback);
+ });
state.PauseTiming();
if (checkHalResult(ret, state)) {
- checkHalResult(mController.off(), state);
+ turnVibratorOff(state);
}
}
});
@@ -402,12 +407,12 @@
public:
static void DefaultArgs(Benchmark* b) {
vibrator::HalController controller;
- auto primitivesResult = controller.getSupportedPrimitives();
+ auto primitivesResult = controller.getInfo().supportedPrimitives;
if (!primitivesResult.isOk()) {
return;
}
- std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+ std::vector<CompositePrimitive> supported = primitivesResult.value();
b->ArgNames({"Primitive"});
if (supported.empty()) {
@@ -415,11 +420,11 @@
return;
}
- for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+ for (const auto& primitive : enum_range<CompositePrimitive>()) {
if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
continue;
}
- if (primitive == hardware::vibrator::CompositePrimitive::NOOP) {
+ if (primitive == CompositePrimitive::NOOP) {
continue;
}
b->Args({static_cast<long>(primitive)});
@@ -430,35 +435,35 @@
bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
auto getPrimitive(const State& state) const {
- return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+ return static_cast<CompositePrimitive>(this->getOtherArg(state, 0));
}
};
BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
- auto result = mController.getCapabilities();
-
- if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
+ if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
return;
}
if (!hasArgs(state)) {
return;
}
- hardware::vibrator::CompositeEffect effect;
+ CompositeEffect effect;
effect.primitive = getPrimitive(state);
effect.scale = 1.0f;
effect.delayMs = static_cast<int32_t>(0);
- std::vector<hardware::vibrator::CompositeEffect> effects;
+ std::vector<CompositeEffect> effects;
effects.push_back(effect);
auto callback = []() {};
for (auto _ : state) {
state.ResumeTiming();
- auto ret = mController.performComposedEffect(effects, callback);
+ auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
+ return hal->performComposedEffect(effects, callback);
+ });
state.PauseTiming();
if (checkHalResult(ret, state)) {
- checkHalResult(mController.off(), state);
+ turnVibratorOff(state);
}
}
});
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index c405545..354e56c 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -29,19 +29,22 @@
std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
+template <typename T>
+using HalFunction = std::function<T(std::shared_ptr<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 HalWrapper {
+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)
- : HalWrapper(std::move(callbackScheduler)),
- mConnector(connector),
- mConnectedHal(nullptr) {}
+ : 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
@@ -51,50 +54,65 @@
*/
virtual bool init();
- /* reloads HAL service instance without waiting. This relies on the HAL version found by 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() override;
+ virtual void tryReconnect();
- virtual HalResult<void> ping() override;
- HalResult<void> on(std::chrono::milliseconds timeout,
- const std::function<void()>& completionCallback) final override;
- HalResult<void> off() final override;
+ /* 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>([](std::shared_ptr<HalWrapper> hal) { return hal->getInfo(); },
+ sDefaultInfo, "getInfo");
+ }
- HalResult<void> setAmplitude(int32_t amplitude) final override;
- HalResult<void> setExternalControl(bool enabled) final override;
-
- HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
- hardware::vibrator::EffectStrength strength) final override;
- HalResult<void> alwaysOnDisable(int32_t id) final override;
-
- HalResult<Capabilities> getCapabilities() final override;
- HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
- final override;
-
- HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) final override;
-
- HalResult<void> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) final override;
+ /* 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>
- HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+ 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;
+ }
- template <typename T>
- using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>;
+ for (int i = 0; i < MAX_RETRIES; i++) {
+ T result = halFn(hal);
+ if (result.checkAndLogFailure(functionName)) {
+ tryReconnect();
+ } else {
+ return result;
+ }
+ }
- template <typename T>
- HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+ return halFn(hal);
+ }
};
}; // namespace vibrator
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 638b483..8720d9d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -48,7 +48,7 @@
if (status.isOk()) {
return HalResult<T>::ok(data);
}
- return HalResult<T>::failed(std::string(status.toString8().c_str()));
+ return HalResult<T>::failed(status.toString8().c_str());
}
static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
@@ -61,10 +61,18 @@
// 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;
@@ -96,6 +104,13 @@
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;
@@ -133,7 +148,8 @@
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,
- ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+ 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) {
@@ -156,6 +172,62 @@
// -------------------------------------------------------------------------------------------------
+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:
@@ -168,34 +240,57 @@
*/
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(int32_t amplitude) = 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<Capabilities> getCapabilities() = 0;
- virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
- virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
- getSupportedPrimitives() = 0;
-
virtual HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) = 0;
- virtual HalResult<void> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
- 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.
@@ -220,43 +315,46 @@
const std::function<void()>& completionCallback) override final;
HalResult<void> off() override final;
- HalResult<void> setAmplitude(int32_t amplitude) 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<Capabilities> getCapabilities() override final;
- HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
- override final;
-
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
- HalResult<void> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ 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;
- std::mutex mCapabilitiesMutex;
- std::mutex mSupportedEffectsMutex;
- std::mutex mSupportedPrimitivesMutex;
sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
- std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
- std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
- GUARDED_BY(mSupportedEffectsMutex);
- std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
- GUARDED_BY(mSupportedPrimitivesMutex);
- // Loads directly from IVibrator handle, skipping caches.
- HalResult<Capabilities> getCapabilitiesInternal();
- HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
sp<hardware::vibrator::IVibrator> getHal();
};
@@ -275,30 +373,18 @@
const std::function<void()>& completionCallback) override final;
HalResult<void> off() override final;
- HalResult<void> setAmplitude(int32_t amplitude) 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;
- HalResult<Capabilities> getCapabilities() override final;
- HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
- override final;
-
- HalResult<void> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) override final;
-
protected:
std::mutex mHandleMutex;
- std::mutex mCapabilitiesMutex;
sp<I> mHandle GUARDED_BY(mHandleMutex);
- std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
- // Loads directly from IVibrator handle, skipping the mCapabilities cache.
- virtual HalResult<Capabilities> getCapabilitiesInternal();
+ virtual HalResult<Capabilities> getCapabilitiesInternal() override;
template <class T>
using perform_fn =
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index ad85990..3294724 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -12,6 +12,15 @@
// 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"],
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 2d9d0d6..279496a 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -31,8 +31,6 @@
#include "test_utils.h"
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
using android::hardware::vibrator::Effect;
using android::hardware::vibrator::EffectStrength;
@@ -42,7 +40,11 @@
using namespace std::chrono_literals;
using namespace testing;
-static constexpr int MAX_ATTEMPTS = 2;
+static const auto ON_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->on(10ms, []() {});
+};
+static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+static const auto PING_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); };
// -------------------------------------------------------------------------------------------------
@@ -58,22 +60,16 @@
(milliseconds timeout, const std::function<void()>& completionCallback),
(override));
MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
- MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (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<vibrator::Capabilities>, getCapabilities, (), (override));
- MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
- MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
- (override));
MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
(Effect effect, EffectStrength strength,
const std::function<void()>& completionCallback),
(override));
- MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect,
- (const std::vector<CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback),
+ MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (),
(override));
vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
@@ -100,56 +96,6 @@
int32_t mConnectCounter;
std::shared_ptr<MockHalWrapper> mMockHal;
std::unique_ptr<vibrator::HalController> mController;
-
- void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects,
- vibrator::HalResult<void> voidResult,
- vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
- vibrator::HalResult<std::vector<Effect>> effectsResult,
- vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
- vibrator::HalResult<milliseconds> durationResult) {
- EXPECT_CALL(*mMockHal.get(), ping())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(), off())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255)))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(),
- alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
- EXPECT_CALL(*mMockHal.get(), getCapabilities())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(capabilitiesResult));
- EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(effectsResult));
- EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(primitivesResult));
- EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(durationResult));
- EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(voidResult));
-
- if (cardinality > 1) {
- // One reconnection call after each failure.
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
- }
- }
};
// -------------------------------------------------------------------------------------------------
@@ -163,110 +109,52 @@
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) {
- std::vector<Effect> effects;
- effects.push_back(Effect::CLICK);
- effects.push_back(Effect::TICK);
- std::vector<CompositePrimitive> primitives;
- primitives.push_back(CompositePrimitive::CLICK);
- primitives.push_back(CompositePrimitive::THUD);
- std::vector<CompositeEffect> compositeEffects;
- compositeEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
- compositeEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+ EXPECT_CALL(*mMockHal.get(), on(_, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
- setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
- vibrator::HalResult<vibrator::Capabilities>::ok(
- vibrator::Capabilities::ON_CALLBACK),
- vibrator::HalResult<std::vector<Effect>>::ok(effects),
- vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
- vibrator::HalResult<milliseconds>::ok(100ms));
-
- ASSERT_TRUE(mController->ping().isOk());
- ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
- ASSERT_TRUE(mController->off().isOk());
- ASSERT_TRUE(mController->setAmplitude(255).isOk());
- ASSERT_TRUE(mController->setExternalControl(true).isOk());
- ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
- ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
-
- auto getCapabilitiesResult = mController->getCapabilities();
- ASSERT_TRUE(getCapabilitiesResult.isOk());
- ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
-
- auto getSupportedEffectsResult = mController->getSupportedEffects();
- ASSERT_TRUE(getSupportedEffectsResult.isOk());
- ASSERT_EQ(effects, getSupportedEffectsResult.value());
-
- auto getSupportedPrimitivesResult = mController->getSupportedPrimitives();
- ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
- ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
-
- auto performEffectResult =
- mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
- ASSERT_TRUE(performEffectResult.isOk());
- ASSERT_EQ(100ms, performEffectResult.value());
-
- ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
+ auto result = mController->doWithRetry<void>(ON_FN, "on");
+ ASSERT_TRUE(result.isOk());
ASSERT_EQ(1, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
- setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(),
- vibrator::HalResult<void>::unsupported(),
- vibrator::HalResult<vibrator::Capabilities>::unsupported(),
- vibrator::HalResult<std::vector<Effect>>::unsupported(),
- vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
- vibrator::HalResult<milliseconds>::unsupported());
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported()));
ASSERT_EQ(0, mConnectCounter);
-
- ASSERT_TRUE(mController->ping().isUnsupported());
- ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
- ASSERT_TRUE(mController->off().isUnsupported());
- ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
- ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
- ASSERT_TRUE(
- mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
- ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
- ASSERT_TRUE(mController->getCapabilities().isUnsupported());
- ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
- ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
- ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
- .isUnsupported());
- ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
- .isUnsupported());
-
+ auto result = mController->doWithRetry<void>(OFF_FN, "off");
+ ASSERT_TRUE(result.isUnsupported());
ASSERT_EQ(1, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
- setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
- vibrator::HalResult<void>::failed("message"),
- vibrator::HalResult<vibrator::Capabilities>::failed("message"),
- vibrator::HalResult<std::vector<Effect>>::failed("message"),
- vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
- vibrator::HalResult<milliseconds>::failed("message"));
+ 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);
- ASSERT_TRUE(mController->ping().isFailed());
- ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
- ASSERT_TRUE(mController->off().isFailed());
- ASSERT_TRUE(mController->setAmplitude(255).isFailed());
- ASSERT_TRUE(mController->setExternalControl(true).isFailed());
- ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
- ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
- ASSERT_TRUE(mController->getCapabilities().isFailed());
- ASSERT_TRUE(mController->getSupportedEffects().isFailed());
- ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
- ASSERT_TRUE(
- mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
- ASSERT_TRUE(
- mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
-
+ auto result = mController->doWithRetry<void>(ON_FN, "on");
+ ASSERT_TRUE(result.isFailed());
ASSERT_EQ(1, mConnectCounter);
}
@@ -283,7 +171,9 @@
}
ASSERT_EQ(0, mConnectCounter);
- ASSERT_TRUE(mController->ping().isOk());
+
+ auto result = mController->doWithRetry<void>(PING_FN, "ping");
+ ASSERT_TRUE(result.isOk());
ASSERT_EQ(1, mConnectCounter);
}
@@ -296,7 +186,10 @@
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
- threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+ 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(); });
@@ -312,31 +205,17 @@
});
ASSERT_EQ(0, mConnectCounter);
- ASSERT_FALSE(mController->init());
- ASSERT_TRUE(mController->ping().isUnsupported());
- ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
- ASSERT_TRUE(mController->off().isUnsupported());
- ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
- ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
- ASSERT_TRUE(
- mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
- ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
- ASSERT_TRUE(mController->getCapabilities().isUnsupported());
- ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
- ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
- ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
- .isUnsupported());
- ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
- .isUnsupported());
+ 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(13, mConnectCounter);
+ ASSERT_EQ(2, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ EXPECT_CALL(*mMockHal.get(), on(_, _))
.Times(Exactly(1))
.WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
@@ -349,14 +228,14 @@
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(1))
.WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- ASSERT_TRUE(mController->on(10ms, callback).isOk());
- ASSERT_TRUE(mController->ping().isFailed());
+ auto onFn = [&](std::shared_ptr<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());
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index f85fa10..d1db82b 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -31,12 +31,14 @@
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;
@@ -74,11 +76,19 @@
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));
@@ -215,19 +225,19 @@
TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2)))
+ 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(FloatNear(0.5, 1e-2)))
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
}
- ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk());
- ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported());
- ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed());
+ 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) {
@@ -289,128 +299,143 @@
ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
}
-TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+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(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .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())));
- ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
- ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+ 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());
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+ 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, TestGetCapabilitiesCachesResult) {
+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())));
-
- 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::Capabilities::ON_CALLBACK, 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::Capabilities::ON_CALLBACK, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
- std::vector<Effect> supportedEffects;
- supportedEffects.push_back(Effect::CLICK);
- supportedEffects.push_back(Effect::TICK);
-
- EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
- .Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
-
- ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
- ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
-
- auto result = mWrapper->getSupportedEffects();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(supportedEffects, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
- std::vector<Effect> supportedEffects;
- supportedEffects.push_back(Effect::CLICK);
- supportedEffects.push_back(Effect::TICK);
-
EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(std::thread([&]() {
- auto result = mWrapper->getSupportedEffects();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(supportedEffects, result.value());
- }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- auto result = mWrapper->getSupportedEffects();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(supportedEffects, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) {
- std::vector<CompositePrimitive> supportedPrimitives;
- supportedPrimitives.push_back(CompositePrimitive::CLICK);
- supportedPrimitives.push_back(CompositePrimitive::THUD);
-
- EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
- .Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
-
- ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
- ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed());
-
- auto result = mWrapper->getSupportedPrimitives();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(supportedPrimitives, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) {
- std::vector<CompositePrimitive> supportedPrimitives;
- supportedPrimitives.push_back(CompositePrimitive::CLICK);
- supportedPrimitives.push_back(CompositePrimitive::THUD);
-
+ 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(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ .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([&]() {
- auto result = mWrapper->getSupportedPrimitives();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(supportedPrimitives, result.value());
- }));
+ threads.push_back(
+ std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
- auto result = mWrapper->getSupportedPrimitives();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(supportedPrimitives, result.value());
+ 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) {
@@ -493,6 +518,9 @@
}
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));
@@ -503,6 +531,19 @@
{
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())));
@@ -520,6 +561,7 @@
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);
@@ -532,3 +574,93 @@
// 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
index 06aa36f..96b2582 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -31,11 +31,13 @@
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;
@@ -147,7 +149,7 @@
TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1)))
+ 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); });
@@ -168,10 +170,11 @@
});
}
- ASSERT_TRUE(mWrapper->setAmplitude(1).isOk());
- ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported());
- ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed());
- ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed());
+ 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) {
@@ -187,7 +190,7 @@
ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
}
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
.Times(Exactly(2))
.WillOnce([]() {
@@ -195,49 +198,52 @@
})
.WillRepeatedly([]() { return hardware::Return<bool>(true); });
- ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ 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, TestGetCapabilitiesWithoutAmplitudeControl) {
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
return hardware::Return<bool>(false);
});
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+ ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
}
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+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([&]() {
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
- }));
+ threads.push_back(
+ std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
- ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) {
- ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+ 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) {
@@ -324,3 +330,18 @@
// 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_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index 08652f4..a6f1a74 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -105,7 +105,7 @@
ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -116,14 +116,12 @@
});
}
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
- result.value());
+ mWrapper->getInfo().capabilities.value());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
@@ -134,12 +132,10 @@
});
}
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
@@ -150,12 +146,10 @@
});
}
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+ ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -166,12 +160,10 @@
});
}
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+ ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -190,11 +182,11 @@
});
}
- ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
- ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -207,20 +199,15 @@
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::Capabilities::AMPLITUDE_CONTROL, result.value());
- }));
+ threads.push_back(
+ std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
}
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
@@ -247,22 +234,16 @@
}
// Call to supportsAmplitudeControl failed.
- auto result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isFailed());
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
// Call to supportsExternalControl failed.
- result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isFailed());
+ ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
// Returns successful result from third call.
- result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
// Returns cached successful result.
- result = mWrapper->getCapabilities();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
}
TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index bcfd15d..3de1576 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -27,6 +27,7 @@
using android::binder::Status;
+using android::hardware::vibrator::Braking;
using android::hardware::vibrator::CompositeEffect;
using android::hardware::vibrator::CompositePrimitive;
using android::hardware::vibrator::Effect;
@@ -34,10 +35,13 @@
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 = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+
class MockBinder : public BBinder {
public:
MOCK_METHOD(status_t, linkToDeath,
@@ -68,11 +72,19 @@
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));
@@ -245,12 +257,11 @@
Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))))
.WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
- EXPECT_CALL(*mMockVibrator.get(), getCapabilities(_))
+ EXPECT_CALL(*mMockVibrator.get(), off())
.Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
.WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ .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);
@@ -260,10 +271,10 @@
auto vibrator = result.value();
// First getVibrator call fails.
ASSERT_FALSE(vibrator->init());
- // First and second getCapabilities calls fail, reload IVibrator with getVibrator.
- ASSERT_FALSE(vibrator->getCapabilities().isOk());
- // Third call to getCapabilities worked after IVibrator reloaded.
- ASSERT_TRUE(vibrator->getCapabilities().isOk());
+ // 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) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
index 6c2aabb..0850ef3 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -41,7 +41,6 @@
virtual ~MockHalController() = default;
MOCK_METHOD(bool, init, (), (override));
- MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
MOCK_METHOD(void, tryReconnect, (), (override));
};
@@ -63,13 +62,9 @@
// -------------------------------------------------------------------------------------------------
TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
- EXPECT_CALL(*mMockController.get(), ping())
- .Times(Exactly(2))
- .WillOnce(Return(vibrator::HalResult<void>::failed("message")))
- .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+ EXPECT_CALL(*mMockController.get(), init()).Times(Exactly(1)).WillOnce(Return(false));
- ASSERT_TRUE(mWrapper->ping().isFailed());
- ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isUnsupported());
}
TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
@@ -85,8 +80,7 @@
}
TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
- std::vector<int32_t> expectedIds;
- expectedIds.push_back(0);
+ std::vector<int> expectedIds = {0};
EXPECT_CALL(*mMockController.get(), init())
.Times(Exactly(2))
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index 8d0b22e..1933a11 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -25,8 +25,12 @@
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;
// -------------------------------------------------------------------------------------------------
@@ -53,6 +57,25 @@
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; };
}
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/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index cb845a0..020b520 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1093,13 +1093,6 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_buffer_count(window, 0);
- if (err != android::OK) {
- ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
- strerror(-err), err);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
-
int swap_interval =
create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
err = window->setSwapInterval(window, swap_interval);
@@ -1707,7 +1700,7 @@
if (err != android::OK) {
ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
} else {
if (img.dequeue_fence >= 0) {
close(img.dequeue_fence);
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index bfc240e..438e5dd 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -842,6 +842,8 @@
bool ret = true;
switch (device->properties.apiVersion ^
VK_VERSION_PATCH(device->properties.apiVersion)) {
+ case VK_API_VERSION_1_2:
+ FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_1:
ret &=
visitor->Visit("subgroupProperties", &device->subgroup_properties) &&
@@ -896,6 +898,8 @@
inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
bool ret = true;
switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) {
+ case VK_API_VERSION_1_2:
+ FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_1:
ret &= visitor->Visit("deviceGroups", &instance->device_groups);
FALLTHROUGH_INTENDED;
@@ -1196,10 +1200,10 @@
std::string* errors) {
*t = T();
Json::Value object(Json::objectValue);
- Json::Reader reader;
- reader.parse(json, object, false);
- if (!object) {
- if (errors) errors->assign(reader.getFormatedErrorMessages());
+ Json::CharReaderBuilder builder;
+ builder["collectComments"] = false;
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ if (!reader->parse(json.data(), json.data() + json.size(), &object, errors)) {
return false;
}
return AsValue(&object, t);
diff --git a/vulkan/vkjson/vkjson_info.cc b/vulkan/vkjson/vkjson_info.cc
deleted file mode 100644
index 3c4b08b..0000000
--- a/vulkan/vkjson/vkjson_info.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-// Copyright (c) 2015-2016 The Khronos Group Inc.
-// Copyright (c) 2015-2016 Valve Corporation
-// Copyright (c) 2015-2016 LunarG, Inc.
-// Copyright (c) 2015-2016 Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 VK_PROTOTYPES
-#define VK_PROTOTYPES
-#endif
-
-#include "vkjson.h"
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <iostream>
-#include <vector>
-
-const uint32_t unsignedNegOne = (uint32_t)(-1);
-
-struct Options {
- bool instance = false;
- uint32_t device_index = unsignedNegOne;
- std::string device_name;
- std::string output_file;
-};
-
-bool ParseOptions(int argc, char* argv[], Options* options) {
- for (int i = 1; i < argc; ++i) {
- std::string arg(argv[i]);
- if (arg == "--instance" || arg == "-i") {
- options->instance = true;
- } else if (arg == "--first" || arg == "-f") {
- options->device_index = 0;
- } else {
- ++i;
- if (i >= argc) {
- std::cerr << "Missing parameter after: " << arg << std::endl;
- return false;
- }
- std::string arg2(argv[i]);
- if (arg == "--device-index" || arg == "-d") {
- int result = sscanf(arg2.c_str(), "%u", &options->device_index);
- if (result != 1) {
- options->device_index = static_cast<uint32_t>(-1);
- std::cerr << "Unable to parse index: " << arg2 << std::endl;
- return false;
- }
- } else if (arg == "--device-name" || arg == "-n") {
- options->device_name = arg2;
- } else if (arg == "--output" || arg == "-o") {
- options->output_file = arg2;
- } else {
- std::cerr << "Unknown argument: " << arg << std::endl;
- return false;
- }
- }
- }
- if (options->instance && (options->device_index != unsignedNegOne ||
- !options->device_name.empty())) {
- std::cerr << "Specifying a specific device is incompatible with dumping "
- "the whole instance." << std::endl;
- return false;
- }
- if (options->device_index != unsignedNegOne && !options->device_name.empty()) {
- std::cerr << "Must specify only one of device index and device name."
- << std::endl;
- return false;
- }
- if (options->instance && options->output_file.empty()) {
- std::cerr << "Must specify an output file when dumping the whole instance."
- << std::endl;
- return false;
- }
- if (!options->output_file.empty() && !options->instance &&
- options->device_index == unsignedNegOne && options->device_name.empty()) {
- std::cerr << "Must specify instance, device index, or device name when "
- "specifying "
- "output file." << std::endl;
- return false;
- }
- return true;
-}
-
-bool Dump(const VkJsonInstance& instance, const Options& options) {
- const VkJsonDevice* out_device = nullptr;
- if (options.device_index != unsignedNegOne) {
- if (static_cast<uint32_t>(options.device_index) >=
- instance.devices.size()) {
- std::cerr << "Error: device " << options.device_index
- << " requested but only " << instance.devices.size()
- << " devices found." << std::endl;
- return false;
- }
- out_device = &instance.devices[options.device_index];
- } else if (!options.device_name.empty()) {
- for (const auto& device : instance.devices) {
- if (device.properties.deviceName == options.device_name) {
- out_device = &device;
- }
- }
- if (!out_device) {
- std::cerr << "Error: device '" << options.device_name
- << "' requested but not found." << std::endl;
- return false;
- }
- }
-
- std::string output_file;
- if (options.output_file.empty()) {
- assert(out_device);
-#if defined(ANDROID)
- output_file.assign("/sdcard/Android/" + std::string(out_device->properties.deviceName));
-#else
- output_file.assign(out_device->properties.deviceName);
-#endif
- output_file.append(".json");
- } else {
- output_file = options.output_file;
- }
- FILE* file = nullptr;
- if (output_file == "-") {
- file = stdout;
- } else {
- file = fopen(output_file.c_str(), "w");
- if (!file) {
- std::cerr << "Unable to open file " << output_file << "." << std::endl;
- return false;
- }
- }
-
- std::string json = out_device ? VkJsonDeviceToJson(*out_device)
- : VkJsonInstanceToJson(instance);
- fwrite(json.data(), 1, json.size(), file);
- fputc('\n', file);
-
- if (output_file != "-") {
- fclose(file);
- std::cout << "Wrote file " << output_file;
- if (out_device)
- std::cout << " for device " << out_device->properties.deviceName;
- std::cout << "." << std::endl;
- }
- return true;
-}
-
-int main(int argc, char* argv[]) {
-#if defined(ANDROID)
- int vulkanSupport = InitVulkan();
- if (vulkanSupport == 0)
- return 1;
-#endif
- Options options;
- if (!ParseOptions(argc, argv, &options))
- return 1;
-
- VkJsonInstance instance = VkJsonGetInstance();
- if (options.instance || options.device_index != unsignedNegOne ||
- !options.device_name.empty()) {
- Dump(instance, options);
- } else {
- for (uint32_t i = 0, n = static_cast<uint32_t>(instance.devices.size()); i < n; i++) {
- options.device_index = i;
- Dump(instance, options);
- }
- }
-
- return 0;
-}
diff --git a/vulkan/vkjson/vkjson_unittest.cc b/vulkan/vkjson/vkjson_unittest.cc
deleted file mode 100644
index de765cd..0000000
--- a/vulkan/vkjson/vkjson_unittest.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-// Copyright (c) 2015-2016 The Khronos Group Inc.
-// Copyright (c) 2015-2016 Valve Corporation
-// Copyright (c) 2015-2016 LunarG, Inc.
-// Copyright (c) 2015-2016 Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 "vkjson.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <iostream>
-
-#define EXPECT(X) if (!(X)) \
- ReportFailure(__FILE__, __LINE__, #X);
-
-#define ASSERT(X) if (!(X)) { \
- ReportFailure(__FILE__, __LINE__, #X); \
- return 2; \
-}
-
-int g_failures;
-
-void ReportFailure(const char* file, int line, const char* assertion) {
- std::cout << file << ":" << line << ": \"" << assertion << "\" failed."
- << std::endl;
- ++g_failures;
-}
-
-int main(int argc, char* argv[]) {
- std::string errors;
- bool result = false;
-
- VkJsonInstance instance;
- instance.devices.resize(1);
- VkJsonDevice& device = instance.devices[0];
-
- const char name[] = "Test device";
- memcpy(device.properties.deviceName, name, sizeof(name));
- device.properties.limits.maxImageDimension1D = 3;
- device.properties.limits.maxSamplerLodBias = 3.5f;
- device.properties.limits.bufferImageGranularity = 0x1ffffffffull;
- device.properties.limits.maxViewportDimensions[0] = 1;
- device.properties.limits.maxViewportDimensions[1] = 2;
- VkFormatProperties format_props = {
- VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
- VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT,
- VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT};
- device.formats.insert(std::make_pair(VK_FORMAT_R8_UNORM, format_props));
- device.formats.insert(std::make_pair(VK_FORMAT_R8G8_UNORM, format_props));
-
- std::string json = VkJsonInstanceToJson(instance);
- std::cout << json << std::endl;
-
- VkJsonInstance instance2;
- result = VkJsonInstanceFromJson(json, &instance2, &errors);
- EXPECT(result);
- if (!result)
- std::cout << "Error: " << errors << std::endl;
- const VkJsonDevice& device2 = instance2.devices.at(0);
-
- EXPECT(!memcmp(&device.properties, &device2.properties,
- sizeof(device.properties)));
- for (auto& kv : device.formats) {
- auto it = device2.formats.find(kv.first);
- EXPECT(it != device2.formats.end());
- EXPECT(!memcmp(&kv.second, &it->second, sizeof(kv.second)));
- }
-
- VkImageFormatProperties props = {};
- json = VkJsonImageFormatPropertiesToJson(props);
- VkImageFormatProperties props2 = {};
- result = VkJsonImageFormatPropertiesFromJson(json, &props2, &errors);
- EXPECT(result);
- if (!result)
- std::cout << "Error: " << errors << std::endl;
-
- EXPECT(!memcmp(&props, &props2, sizeof(props)));
-
- if (g_failures) {
- std::cout << g_failures << " failures." << std::endl;
- return 1;
- } else {
- std::cout << "Success." << std::endl;
- return 0;
- }
-}