Merge changes Id305effe,I065f1968,I18b6d921 into sc-dev
* changes:
SF: Only buildCachedSets if (mergeWithCachedSets)
Remove Flattener::mLayersHash
Mark CachedSet::mFingerprint as const
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 922367f..8bcb1e5 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/idlcli/
+ cmds/servicemanager/
include/input/
include/powermanager/
libs/binder/fuzzer/
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 7ab2a8d..e2ffd02 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2175,14 +2175,13 @@
}
/*
- * mount debugfs for non-user builds which launch with S and unmount it
- * after invoking dumpstateBoard_* methods. This is to enable debug builds
- * to not have debugfs mounted during runtime. It will also ensure that
- * debugfs is only accessed by the dumpstate HAL.
+ * mount debugfs for non-user builds with ro.product.enforce_debugfs_restrictions
+ * set to true and unmount it after invoking dumpstateBoard_* methods.
+ * This is to enable debug builds to not have debugfs mounted during runtime.
+ * It will also ensure that debugfs is only accessed by the dumpstate HAL.
*/
- auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
- bool mount_debugfs = !PropertiesHelper::IsUserBuild() && api_level >= 31;
-
+ auto mount_debugfs =
+ android::base::GetBoolProperty("ro.product.enforce_debugfs_restrictions", false);
if (mount_debugfs) {
RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
AS_ROOT_20);
@@ -2290,7 +2289,10 @@
}
if (mount_debugfs) {
- RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
+ auto keep_debugfs_mounted =
+ android::base::GetProperty("persist.dbg.keep_debugfs_mounted", "");
+ if (keep_debugfs_mounted.empty())
+ RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
}
auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 2c573e4..db508b5 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1032,12 +1032,12 @@
ZipArchiveHandle handle_;
};
-// Generate a quick wifi report redirected to a file, open it and verify entry exist.
-TEST_F(ZippedBugReportStreamTest, StreamWifiReport) {
- std::string out_path = kTestDataPath + "out.zip";
+// Generate a quick LimitedOnly report redirected to a file, open it and verify entry exist.
+TEST_F(ZippedBugReportStreamTest, StreamLimitedOnlyReport) {
+ std::string out_path = kTestDataPath + "StreamLimitedOnlyReportOut.zip";
android::base::unique_fd out_fd;
CreateFd(out_path, &out_fd);
- ds_.options_->wifi_only = true;
+ ds_.options_->limited_only = true;
ds_.options_->stream_to_socket = true;
RedirectOutputToFd(out_fd);
@@ -1051,7 +1051,7 @@
ExtractToMemory(handle_, &entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
entry.uncompressed_length);
EXPECT_THAT(bugreport_txt_name,
- testing::ContainsRegex("(bugreport-.+-wifi(-[[:digit:]]+){6}\\.txt)"));
+ testing::ContainsRegex("(bugreport-.+(-[[:digit:]]+){6}\\.txt)"));
VerifyEntry(handle_, bugreport_txt_name, &entry);
}
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/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 379cf92..3a87776 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -20,21 +20,19 @@
#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/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
#include <selinux/android.h>
-#include <apex_file_repository.h>
-#include <apex_constants.h>
-#include <apexd.h>
-
#include "installd_constants.h"
#include "otapreopt_utils.h"
@@ -64,47 +62,23 @@
}
}
-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<std::string> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir,
- apex::kApexPackageVendorDir};
- // Initialize ApexFileRepository used internally in ScanPackagesDirAndActivate.
- // This is a quick fix to fix apex activation in otapreopt_chroot.
- apex::ApexFileRepository::GetInstance().AddPreInstalledApex(apex_dirs);
- for (const auto& dir : apex_dirs) {
- // Cast call to void to suppress warn_unused_result.
- static_cast<void>(apex::ScanPackagesDirAndActivate(dir.c_str()));
+static void ActivateApexPackages() {
+ std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--otachroot-bootstrap"};
+ std::string apexd_error_msg;
+
+ bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
+ if (!exec_result) {
+ PLOG(ERROR) << "Running otapreopt failed: " << apexd_error_msg;
+ exit(220);
}
- return apex::GetActivePackages();
}
-static void CreateApexInfoList(const std::vector<apex::ApexFile>& apex_files) {
- // Setup the apex-info-list.xml file
- const std::string apex_info_file = std::string(apex::kApexRoot) + "/" + apex::kApexInfoList;
- std::fstream xml(apex_info_file.c_str(), std::ios::out | std::ios::trunc);
- if (!xml.is_open()) {
- PLOG(ERROR) << "Failed to open " << apex_info_file;
- exit(216);
- }
-
- // we do not care about inactive apexs
- std::vector<apex::ApexFile> inactive;
- apex::CollectApexInfoList(xml, apex_files, inactive);
- xml.flush();
- xml.close();
-}
-
-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();
- }
+static void DeactivateApexPackages() {
+ std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--unmount-all"};
+ std::string apexd_error_msg;
+ bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
+ if (!exec_result) {
+ PLOG(ERROR) << "Running /system/bin/apexd --unmount-all failed: " << apexd_error_msg;
}
}
@@ -267,27 +241,57 @@
exit(205);
}
+ // Call apexd --unmount-all to free up loop and dm block devices, so that we can re-use
+ // them during the next invocation. Since otapreopt_chroot calls exit in case something goes
+ // wrong we need to register our own atexit handler.
+ // We want to register this handler before actually activating apex packages. This is mostly
+ // due to the fact that if fail to unmount apexes, then on the next run of otapreopt_chroot
+ // we will ask for new loop devices instead of re-using existing ones, and we really don't want
+ // to do that. :)
+ if (atexit(DeactivateApexPackages) != 0) {
+ LOG(ERROR) << "Failed to register atexit hander";
+ exit(206);
+ }
+
// 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();
- CreateApexInfoList(active_packages);
+ ActivateApexPackages();
+ auto cleanup = android::base::make_scope_guard([](){
+ std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--unmount-all"};
+ std::string apexd_error_msg;
+ bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
+ if (!exec_result) {
+ PLOG(ERROR) << "Running /system/bin/apexd --unmount-all failed: " << apexd_error_msg;
+ }
+ });
// Check that an ART APEX has been activated; clean up and exit
// early otherwise.
static constexpr const std::string_view kRequiredApexs[] = {
"com.android.art",
"com.android.runtime",
};
- for (std::string_view apex : kRequiredApexs) {
- if (std::none_of(active_packages.begin(), active_packages.end(),
- [&](const apex::ApexFile& package) {
- return package.GetManifest().name() == apex;
- })) {
- LOG(FATAL_WITHOUT_ABORT) << "No activated " << apex << " APEX package.";
- DeactivateApexPackages(active_packages);
- exit(217);
+ 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) {
@@ -323,9 +327,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/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 17ea903..a27fd10 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -86,7 +86,7 @@
bool generate_compact_dex,
bool use_jitzygote_image,
const char* compilation_reason) {
- PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
+ PrepareBootImageFlags(use_jitzygote_image);
PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
dex_metadata, profile, swap_fd, class_loader_context,
@@ -112,7 +112,7 @@
RunDex2Oat::~RunDex2Oat() {}
-void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) {
std::string boot_image;
if (use_jitzygote_image) {
boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
@@ -120,23 +120,6 @@
boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
}
AddArg(boot_image);
-
- // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
- // BOOTCLASSPATH.
- char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
- if (dex2oat_bootclasspath != nullptr) {
- AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
- }
-
- std::string updatable_bcp_packages =
- MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
- "--updatable-bcp-packages-file=%s");
- if (updatable_bcp_packages.empty()) {
- // Make dex2oat fail by providing non-existent file name.
- updatable_bcp_packages =
- "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
- }
- AddArg(updatable_bcp_packages);
}
void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 325a3a2..475e124 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -56,7 +56,7 @@
void Exec(int exit_code);
protected:
- void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+ void PrepareBootImageFlags(bool use_jitzygote_image);
void PrepareInputFileFlags(const UniqueFile& output_oat,
const UniqueFile& output_vdex,
const UniqueFile& output_image,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 3813cf7..0a638cd 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -175,8 +175,6 @@
default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
- default_expected_flags_["--updatable-bcp-packages-file"] =
- "=/nonx/updatable-bcp-packages.txt";
// Arch
default_expected_flags_["--instruction-set"] = "=arm64";
@@ -320,28 +318,6 @@
VerifyExpectedFlags();
}
-TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
- ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
- ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
- << "Failed to setenv: " << strerror(errno);
-
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
- VerifyExpectedFlags();
-
- ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
- << "Failed to setenv: " << strerror(errno);
-}
-
-TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
- setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
- VerifyExpectedFlags();
-}
-
TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
auto args = RunDex2OatArgs::MakeDefaultTestArgs();
args->generate_compact_dex = false;
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index fbf1e0c..e272025 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -351,7 +351,7 @@
uid = kTestAppUid;
}
if (class_loader_context == nullptr) {
- class_loader_context = "&";
+ class_loader_context = "PCL[]";
}
int32_t dexopt_needed = 0; // does not matter;
std::optional<std::string> out_path; // does not matter
@@ -478,7 +478,7 @@
bool should_binder_call_succeed,
/*out */ binder::Status* binder_result) {
std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
- std::string class_loader_context = "&";
+ std::string class_loader_context = "PCL[]";
int32_t target_sdk_version = 0; // default
std::string profile_name = "primary.prof";
std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt;
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c47df52..c4ecd07 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1062,6 +1062,8 @@
static const char* kProcFilesystems = "/proc/filesystems";
bool supports_sdcardfs() {
+ if (!property_get_bool("external_storage.sdcardfs.enabled", true))
+ return false;
std::string supported;
if (!android::base::ReadFileToString(kProcFilesystems, &supported)) {
PLOG(ERROR) << "Failed to read supported filesystems";
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index b6ff28d..7c1ca91 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -508,10 +508,10 @@
EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6"));
EXPECT_EQ("", err.str());
+ std::string error;
vintf::HalManifest m;
- EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
- << "--init-vintf does not emit valid HAL manifest: "
- << vintf::gHalManifestConverter.lastError();
+ EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str(), &error))
+ << "--init-vintf does not emit valid HAL manifest: " << error;
}
// test default columns
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 9de344a..3ebdeee 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -14,6 +14,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
srcs: [
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 0dbab4e..2f55249 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -239,7 +239,8 @@
#endif // !VENDORSERVICEMANAGER
// implicitly unlinked when the binder is removed
- if (binder->remoteBinder() != nullptr && binder->linkToDeath(this) != OK) {
+ if (binder->remoteBinder() != nullptr &&
+ binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
LOG(ERROR) << "Could not linkToDeath when adding " << name;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -307,7 +308,9 @@
return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}
- if (OK != IInterface::asBinder(callback)->linkToDeath(this)) {
+ if (OK !=
+ IInterface::asBinder(callback)->linkToDeath(
+ sp<ServiceManager>::fromExisting(this))) {
LOG(ERROR) << "Could not linkToDeath when adding " << name;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -461,7 +464,8 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
- if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+ if (OK !=
+ IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -491,7 +495,7 @@
}
ssize_t ServiceManager::Service::getNodeStrongRefCount() {
- sp<BpBinder> bpBinder = binder->remoteBinder();
+ sp<BpBinder> bpBinder = sp<BpBinder>::fromExisting(binder->remoteBinder());
if (bpBinder == nullptr) return -1;
return ProcessState::self()->getStrongRefCountForNode(bpBinder);
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 627dfe6..8c1beac 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -39,7 +39,7 @@
class BinderCallback : public LooperCallback {
public:
static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
- sp<BinderCallback> cb = new BinderCallback;
+ sp<BinderCallback> cb = sp<BinderCallback>::make();
int binder_fd = -1;
IPCThreadState::self()->setupPolling(&binder_fd);
@@ -65,7 +65,7 @@
class ClientCallbackCallback : public LooperCallback {
public:
static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
- sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+ sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
@@ -105,6 +105,7 @@
return 1; // Continue receiving callbacks.
}
private:
+ friend sp<ClientCallbackCallback>;
ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
sp<ServiceManager> mManager;
};
@@ -120,7 +121,7 @@
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
- sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
+ sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index fb9f9df..5d5a75e 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -46,7 +46,7 @@
}
};
- return new LinkableBinder;
+ return sp<LinkableBinder>::make();
}
class MockAccess : public Access {
@@ -71,7 +71,7 @@
ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
ON_CALL(*access, canList(_)).WillByDefault(Return(true));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
return sm;
}
@@ -119,7 +119,7 @@
.uid = uid,
}));
EXPECT_CALL(*access, canAdd(_, _)).Times(0);
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -161,7 +161,7 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -194,7 +194,7 @@
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -218,7 +218,7 @@
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
sp<IBinder> service = getBinder();
EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
@@ -244,7 +244,7 @@
// TODO(b/136023468): when security check is first, this should be called first
// EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -261,7 +261,7 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+ sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
std::vector<std::string> out;
EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
@@ -329,9 +329,9 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(),
Status::EX_SECURITY);
@@ -343,9 +343,9 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
// should always hit security error first
EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
@@ -355,7 +355,7 @@
TEST(ServiceNotifications, InvalidName) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(),
Status::EX_ILLEGAL_ARGUMENT);
@@ -371,7 +371,7 @@
TEST(ServiceNotifications, Unregister) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0);
@@ -380,7 +380,7 @@
TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
Status::EX_ILLEGAL_STATE);
@@ -389,7 +389,7 @@
TEST(ServiceNotifications, NoNotification) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
EXPECT_TRUE(sm->addService("otherservice", getBinder(),
@@ -402,7 +402,7 @@
TEST(ServiceNotifications, GetNotification) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
sp<IBinder> service = getBinder();
@@ -417,7 +417,7 @@
TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
sp<IBinder> service = getBinder();
@@ -433,7 +433,7 @@
TEST(ServiceNotifications, GetMultipleNotification) {
auto sm = getPermissiveServiceManager();
- sp<CallbackHistorian> cb = new CallbackHistorian;
+ sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
sp<IBinder> binder1 = getBinder();
sp<IBinder> binder2 = getBinder();
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 79aab82..06afefd 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -50,7 +50,6 @@
CornerRadiusChange corner_radius = 16;
ReparentChange reparent = 17;
RelativeParentChange relative_parent = 18;
- ReparentChildrenChange reparent_children = 19;
BackgroundBlurRadiusChange background_blur_radius = 20;
ShadowRadiusChange shadow_radius = 21;
BlurRegionsChange blur_regions = 22;
@@ -190,10 +189,6 @@
required int32 parent_id = 1;
}
-message ReparentChildrenChange {
- required int32 parent_id = 1;
-}
-
message RelativeParentChange {
required int32 relative_parent_id = 1;
required int32 z = 2;
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index bbbe6f7..a6d9a3f 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -410,9 +410,6 @@
case SurfaceChange::SurfaceChangeCase::kReparent:
setReparentChange(transaction, change.id(), change.reparent());
break;
- case SurfaceChange::SurfaceChangeCase::kReparentChildren:
- setReparentChildrenChange(transaction, change.id(), change.reparent_children());
- break;
case SurfaceChange::SurfaceChangeCase::kRelativeParent:
setRelativeParentChange(transaction, change.id(), change.relative_parent());
break;
@@ -709,15 +706,6 @@
t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z());
}
-void Replayer::setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ReparentChildrenChange& c) {
- if (mLayers.count(c.parent_id()) == 0 || mLayers[c.parent_id()] == nullptr) {
- ALOGE("Layer %d not found in reparent children transaction", c.parent_id());
- return;
- }
- t.reparentChildren(mLayers[id], mLayers[c.parent_id()]);
-}
-
void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ShadowRadiusChange& c) {
t.setShadowRadius(mLayers[id], c.radius());
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 324d591..252db2b 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -116,8 +116,6 @@
layer_id id, const ReparentChange& c);
void setRelativeParentChange(SurfaceComposerClient::Transaction& t,
layer_id id, const RelativeParentChange& c);
- void setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ReparentChildrenChange& c);
void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ShadowRadiusChange& c);
void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/android.software.translation.xml b/data/etc/android.software.translation.xml
deleted file mode 100644
index 3b361e5..0000000
--- a/data/etc/android.software.translation.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<permissions>
- <feature name="android.software.translation" />
-</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index c3c3a7f..41f1514 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -54,7 +54,6 @@
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.secure_lock_screen" />
- <feature name="android.software.translation" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 4b565fd..8c369de 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -54,7 +54,6 @@
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.secure_lock_screen" />
- <feature name="android.software.translation" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index a70dffd..6704a1d 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -241,6 +241,7 @@
*
* Available since API level 30.
*
+ * @param env Handle to the JNI environment pointer.
* @param bitmap Handle to an android.graphics.Bitmap.
* @param outBuffer On success, is set to a pointer to the
* {@link AHardwareBuffer} associated with bitmap. This acquires
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index cc5420e..b743f49 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -32,6 +32,11 @@
__BEGIN_DECLS
struct AChoreographer;
+/**
+ * Opaque type that provides access to an AChoreographer object.
+ *
+ * A pointer can be obtained using {@link AChoreographer_getInstance()}.
+ */
typedef struct AChoreographer AChoreographer;
/**
diff --git a/include/android/font.h b/include/android/font.h
index a172618..8a3a474 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -189,7 +189,7 @@
* Available since API level 29.
*
* \param font a font object. Passing NULL is not allowed.
- * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
+ * \return a positive integer less than or equal to {@link AFONT_WEIGHT_MAX} is returned.
*/
uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -241,7 +241,7 @@
* In this case, AFont_getAxisCount returns 2 and AFont_getAxisTag
* and AFont_getAxisValue will return following values.
* \code{.cpp}
- * AFont* font = AFontIterator_next(ite);
+ * AFont* font = ASystemFontIterator_next(ite);
*
* // Returns the number of axes
* AFont_getAxisCount(font); // Returns 2
@@ -289,7 +289,7 @@
*
* \param font a font object. Passing NULL is not allowed.
* \param axisIndex an index to the font variation settings. Passing value larger than or
- * equal to {@link ASYstemFont_getAxisCount} is not allwed.
+ * equal to {@link AFont_getAxisCount} is not allowed.
* \return a float value for the given font variation setting.
*/
float AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex)
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 49e478c..4417422 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -36,7 +36,7 @@
* // Simple font query for the ASCII character.
* std::vector<uint16_t> text = { 'A' };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a valid font file.
* AFontMatcher_destroy(matcher);
*
@@ -44,17 +44,17 @@
* std::vector<uint16_t> text = { 0x9AA8 };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
* AFontMatcher_setLocales(matcher, "zh-CN,ja-JP");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Simplified Chinese font.
* AFontMatcher_setLocales(matcher, "ja-JP,zh-CN");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Japanese font.
* AFontMatcher_destroy(matcher);
*
* // Querying font for text/color emoji
* std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 8 and the font will points a color emoji font.
* AFontMatcher_destroy(matcher);
*
@@ -62,7 +62,7 @@
* // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
* std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
* AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Hebrew font.
* AFontMatcher_destroy(matcher);
* \endcode
@@ -146,7 +146,7 @@
/**
* Set font style to matcher.
*
- * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL}
+ * If this function is not called, the matcher performs with {@link AFONT_WEIGHT_NORMAL}
* with non-italic style.
*
* Available since API level 29.
@@ -206,7 +206,7 @@
* \param textLength a length of the given text buffer. This must not be zero.
* \param runLengthOut if not null, the font run length will be filled.
* \return a font to be used for given text and params. You need to release the returned font by
- * ASystemFont_close when it is no longer needed.
+ * AFont_close when it is no longer needed.
*/
AFont* _Nonnull AFontMatcher_match(
const AFontMatcher* _Nonnull matcher,
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index eb9c528..c5e3587 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -286,6 +286,9 @@
* The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
* for closing it.
*
+ * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
+ * as the surface control might be composited using the GPU.
+ *
* Available since API level 29.
*/
void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
@@ -452,10 +455,10 @@
__INTRODUCED_IN(29);
/**
- * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control,
- * frameRate, compatibility, true).
+ * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
+ * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
*
- * See ASurfaceTransaction_setFrameRateWithSeamlessness().
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy().
*
* Available since API level 30.
*/
@@ -483,19 +486,46 @@
* influence the system's choice of display frame rate. To specify a compatibility use the
* ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
*
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, float frameRate,
- int8_t compatibility, bool shouldBeSeamless)
+ int8_t compatibility, int8_t changeFrameRateStrategy)
__INTRODUCED_IN(31);
+/**
+ * Indicate whether to enable backpressure for buffer submission to a given SurfaceControl.
+ *
+ * By default backpressure is disabled, which means submitting a buffer prior to receiving
+ * a callback for the previous buffer could lead to that buffer being "dropped". In cases
+ * where you are selecting for latency, this may be a desirable behavior! We had a new buffer
+ * ready, why shouldn't we show it?
+ *
+ * When back pressure is enabled, each buffer will be required to be presented
+ * before it is released and the callback delivered
+ * (absent the whole SurfaceControl being removed).
+ *
+ * Most apps are likely to have some sort of backpressure internally, e.g. you are
+ * waiting on the callback from frame N-2 before starting frame N. In high refresh
+ * rate scenarios there may not be much time between SurfaceFlinger completing frame
+ * N-1 (and therefore releasing buffer N-2) and beginning frame N. This means
+ * your app may not have enough time to respond in the callback. Using this flag
+ * and pushing buffers earlier for server side queuing will be advantageous
+ * in such cases.
+ *
+ * \param transaction The transaction in which to make the change.
+ * \param surface_control The ASurfaceControl on which to control buffer backpressure behavior.
+ * \param enableBackPressure Whether to enable back pressure.
+ */
+void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ bool enableBackPressure)
+ __INTRODUCED_IN(31);
+
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/input/Input.h b/include/input/Input.h
index f9fe6b9..bb5ca0e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -266,6 +266,20 @@
const char* motionClassificationToString(MotionClassification classification);
/**
+ * Portion of FrameMetrics timeline of interest to input code.
+ */
+enum GraphicsTimeline : size_t {
+ /** Time when the app sent the buffer to SurfaceFlinger. */
+ GPU_COMPLETED_TIME = 0,
+
+ /** Time when the frame was presented on the display */
+ PRESENT_TIME = 1,
+
+ /** Total size of the 'GraphicsTimeline' array. Must always be last. */
+ SIZE = 2
+};
+
+/**
* Generator of unique numbers used to identify input events.
*
* Layout of ID:
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 2deb99d..712adfa 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -177,6 +177,14 @@
int32_t ordinal;
};
+struct InputDeviceBatteryInfo {
+ explicit InputDeviceBatteryInfo(std::string name, int32_t id) : name(name), id(id) {}
+ // Name string of the battery.
+ std::string name;
+ // Battery id
+ int32_t id;
+};
+
/*
* Describes the characteristics and capabilities of an input device.
*/
@@ -219,6 +227,7 @@
float min, float max, float flat, float fuzz, float resolution);
void addMotionRange(const MotionRange& range);
void addSensorInfo(const InputDeviceSensorInfo& info);
+ void addBatteryInfo(const InputDeviceBatteryInfo& info);
void addLightInfo(const InputDeviceLightInfo& info);
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
@@ -276,6 +285,8 @@
std::unordered_map<InputDeviceSensorType, InputDeviceSensorInfo> mSensors;
/* Map from light ID to light info */
std::unordered_map<int32_t, InputDeviceLightInfo> mLights;
+ /* Map from battery ID to battery info */
+ std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries;
};
/* Types of input device configuration files. */
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 3e5674e..898d1a9 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -71,6 +71,7 @@
FOCUS,
CAPTURE,
DRAG,
+ TIMELINE,
};
struct Header {
@@ -195,6 +196,14 @@
inline size_t size() const { return sizeof(Drag); }
} drag;
+
+ struct Timeline {
+ int32_t eventId;
+ uint32_t empty;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+
+ inline size_t size() const { return sizeof(Timeline); }
+ } timeline;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -381,10 +390,25 @@
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.
+ struct Timeline {
+ int32_t inputEventId;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ };
+
+ typedef std::variant<Finished, Timeline> ConsumerResponse;
+ /* Receive a signal from the consumer in reply to the original dispatch signal.
+ * If a signal was received, returns a Finished or a Timeline object.
+ * The InputConsumer should return a Finished object for every InputMessage that it is sent
+ * to confirm that it has been processed and that the InputConsumer is responsive.
+ * If several InputMessages are sent to InputConsumer, it's possible to receive Finished
+ * events out of order for those messages.
*
- * The returned sequence number is never 0 unless the operation failed.
+ * The Timeline object is returned whenever the receiving end has processed a graphical frame
+ * and is returning the timeline of the frame. Not all input events will cause a Timeline
+ * object to be returned, and there is not guarantee about when it will arrive.
+ *
+ * If an object of Finished is returned, the returned sequence number is never 0 unless the
+ * operation failed.
*
* Returned error codes:
* OK on success.
@@ -392,7 +416,7 @@
* DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- android::base::Result<Finished> receiveFinishedSignal();
+ android::base::Result<ConsumerResponse> receiveConsumerResponse();
private:
std::shared_ptr<InputChannel> mChannel;
@@ -448,6 +472,9 @@
*/
status_t sendFinishedSignal(uint32_t seq, bool handled);
+ status_t sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
/* Returns true if there is a deferred event waiting.
*
* Should be called after calling consume() to determine whether the consumer
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index dd34c0a..71a36d0 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -20,6 +20,7 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalWrapper.h>
@@ -54,8 +55,12 @@
void init();
- virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
private:
std::mutex mConnectedHalMutex;
@@ -67,7 +72,8 @@
const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
std::shared_ptr<HalWrapper> initHal();
- HalResult processHalResult(HalResult result, const char* functionName);
+ template <typename T>
+ HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
};
// -------------------------------------------------------------------------------------------------
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index c3e7601..2c6eacb 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -21,6 +21,7 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/Mode.h>
namespace android {
@@ -34,11 +35,81 @@
OFF = 2,
};
-// State of the Power HAL api call result.
-enum class HalResult {
- SUCCESSFUL = 0,
- FAILED = 1,
- UNSUPPORTED = 2,
+// Result of a call to the Power HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T value) { return HalResult(value); }
+ static HalResult<T> failed(std::string msg) {
+ return HalResult(std::move(msg), /* unsupported= */ false);
+ }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(binder::Status status, T data) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
+ }
+ static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T data);
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T value)
+ : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Power HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(status_t status);
+ static HalResult<void> fromStatus(binder::Status status);
+ static HalResult<void> fromStatus(hardware::power::V1_0::Status status);
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
};
// Wrapper for Power HAL handlers.
@@ -46,8 +117,12 @@
public:
virtual ~HalWrapper() = default;
- virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
- virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0;
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
};
// Empty Power HAL wrapper that ignores all api calls.
@@ -56,8 +131,12 @@
EmptyHalWrapper() = default;
~EmptyHalWrapper() = default;
- virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
};
// Wrapper for the HIDL Power HAL v1.0.
@@ -67,16 +146,20 @@
: mHandleV1_0(std::move(Hal)) {}
virtual ~HidlHalWrapperV1_0() = default;
- virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
protected:
- virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
private:
sp<hardware::power::V1_0::IPower> mHandleV1_0;
- HalResult setInteractive(bool enabled);
- HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled);
+ HalResult<void> setInteractive(bool enabled);
+ HalResult<void> setFeature(hardware::power::V1_0::Feature feature, bool enabled);
};
// Wrapper for the HIDL Power HAL v1.1.
@@ -88,8 +171,8 @@
virtual ~HidlHalWrapperV1_1() = default;
protected:
- virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
- uint32_t data) override;
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ uint32_t data) override;
private:
sp<hardware::power::V1_1::IPower> mHandleV1_1;
@@ -101,8 +184,12 @@
explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
virtual ~AidlHalWrapper() = default;
- virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<int64_t> getHintSessionPreferredRate() override;
private:
// Control access to the boost and mode supported arrays.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index a17e482..6da4e9e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -58,21 +58,15 @@
// transport itself and should be moved to AIDL or in domain-specific libs.
//
// Currently, these are only on system android (not vendor, not host)
+// TODO(b/183654927) - move these into separate libraries
libbinder_device_interface_sources = [
- "ActivityManager.cpp",
"AppOpsManager.cpp",
- "IActivityManager.cpp",
"IAppOpsCallback.cpp",
"IAppOpsService.cpp",
- "IBatteryStats.cpp",
- "IMediaResourceMonitor.cpp",
+
"IPermissionController.cpp",
- "IProcessInfoService.cpp",
- "IUidObserver.cpp",
"PermissionCache.cpp",
"PermissionController.cpp",
- "ProcessInfoService.cpp",
- "IpPrefix.cpp",
]
cc_library {
@@ -121,13 +115,17 @@
"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",
- ":activity_manager_procstate_aidl",
],
target: {
@@ -162,6 +160,7 @@
"-Werror",
"-Wzero-as-null-pointer-constant",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
product_variables: {
binder32bit: {
@@ -228,9 +227,6 @@
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",
@@ -239,6 +235,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,
@@ -251,3 +257,56 @@
},
},
}
+
+// libbinder historically contained additional interfaces that provided specific
+// functionality in the platform but have nothing to do with binder itself. These
+// are moved out of libbinder in order to avoid the overhead of their vtables.
+// If you are working on or own one of these interfaces, the responsible things
+// to would be:
+// - give them a new home
+// - convert them to AIDL instead of having manually written parceling code
+
+cc_library {
+ name: "libbatterystats_aidl",
+ srcs: [
+ "IBatteryStats.cpp",
+ ],
+ export_include_dirs: ["include_batterystats"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+cc_library {
+ name: "libprocessinfoservice_aidl",
+ srcs: [
+ "IProcessInfoService.cpp",
+ "ProcessInfoService.cpp",
+ ],
+ export_include_dirs: ["include_processinfo"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "liblog",
+ ],
+}
+
+cc_library {
+ name: "libactivitymanager_aidl",
+ srcs: [
+ "ActivityManager.cpp",
+ "IActivityManager.cpp",
+ "IUidObserver.cpp",
+ ":activity_manager_procstate_aidl",
+ ],
+ export_include_dirs: ["include_activitymanager"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "liblog",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ },
+}
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index de42f36..baa9d75 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -37,7 +37,7 @@
pthread_mutex_lock(&gClientIdMutex);
if (gClientId == nullptr) {
- gClientId = new BBinder();
+ gClientId = sp<BBinder>::make();
}
pthread_mutex_unlock(&gClientIdMutex);
return gClientId;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index ddda024..fdcf94a 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>
@@ -106,8 +107,7 @@
// ---------------------------------------------------------------------------
-
-BpBinder* BpBinder::create(int32_t handle) {
+sp<BpBinder> BpBinder::create(int32_t handle) {
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
trackedUid = IPCThreadState::self()->getCallingUid();
@@ -133,25 +133,56 @@
}
sTrackingMap[trackedUid]++;
}
- return new BpBinder(handle, trackedUid);
+ return sp<BpBinder>::make(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);
+sp<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 sp<BpBinder>::make(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 {
@@ -162,10 +193,13 @@
const String16& BpBinder::getInterfaceDescriptor() const
{
if (isDescriptorCached() == false) {
- Parcel send, reply;
+ sp<BpBinder> thiz = sp<BpBinder>::fromExisting(const_cast<BpBinder*>(this));
+
+ Parcel data;
+ data.markForBinder(thiz);
+ Parcel reply;
// do the IPC without a lock held.
- status_t err = const_cast<BpBinder*>(this)->transact(
- INTERFACE_TRANSACTION, send, &reply);
+ status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply);
if (err == NO_ERROR) {
String16 res(reply.readString16());
Mutex::Autolock _l(mLock);
@@ -190,9 +224,10 @@
status_t BpBinder::pingBinder()
{
- Parcel send;
+ Parcel data;
+ data.markForBinder(sp<BpBinder>::fromExisting(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 +271,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 +290,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 +309,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 +328,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 +347,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 +363,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 +374,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;
}
@@ -356,7 +402,7 @@
ALOGV("Reporting death to recipient: %p\n", recipient.get());
if (recipient == nullptr) return;
- recipient->binderDied(this);
+ recipient->binderDied(wp<BpBinder>::fromExisting(this));
}
@@ -388,7 +434,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 +444,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 +462,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 +496,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 +511,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 349658e..a90bfd2 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -254,7 +254,7 @@
BufferState* bs = ts.states[mIndex].get();
if (bs != nullptr && bs->seq == mSeq) return bs;
- ts.states.editItemAt(mIndex) = new BufferState(mIndex);
+ ts.states.editItemAt(mIndex) = sp<BufferState>::make(mIndex);
bs = ts.states[mIndex].get();
if (bs != nullptr) return bs;
}
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index e4ac4b4..8676955 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -26,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/Debug.h b/libs/binder/Debug.h
index ac71e00..7ca087e 100644
--- a/libs/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/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 1897969..d59f445 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -18,8 +18,8 @@
#include <binder/IAppOpsService.h>
-#include <utils/Log.h>
#include <binder/Parcel.h>
+#include <utils/Log.h>
#include <utils/String8.h>
#include <optional>
@@ -63,6 +63,9 @@
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ // TODO b/184855056: extract to class
+ reply.readInt32();
+ reply.readByte();
return reply.readInt32();
}
@@ -84,6 +87,9 @@
remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+ // TODO b/184855056: extract to class
+ reply.readInt32();
+ reply.readByte();
return reply.readInt32();
}
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index d0085df..0de804c 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
#include <utils/Log.h>
#include <binder/Parcel.h>
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index b19004d..2780bd4 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -33,14 +33,14 @@
sp<IBinder> IInterface::asBinder(const IInterface* iface)
{
if (iface == nullptr) return nullptr;
- return const_cast<IInterface*>(iface)->onAsBinder();
+ return sp<IBinder>::fromExisting(const_cast<IInterface*>(iface)->onAsBinder());
}
// static
sp<IBinder> IInterface::asBinder(const sp<IInterface>& iface)
{
if (iface == nullptr) return nullptr;
- return iface->onAsBinder();
+ return sp<IBinder>::fromExisting(iface->onAsBinder());
}
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
deleted file mode 100644
index f5fa817..0000000
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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/IMediaResourceMonitor.h>
-#include <binder/Parcel.h>
-#include <utils/Errors.h>
-#include <sys/types.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
-public:
- explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
- : BpInterface<IMediaResourceMonitor>(impl) {}
-
- virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaResourceMonitor::getInterfaceDescriptor());
- data.writeInt32(pid);
- data.writeInt32(type);
- remote()->transact(NOTIFY_RESOURCE_GRANTED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor")
-
-// ----------------------------------------------------------------------
-
-// NOLINTNEXTLINE(google-default-arguments)
-status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch(code) {
- case NOTIFY_RESOURCE_GRANTED: {
- CHECK_INTERFACE(IMediaResourceMonitor, data, reply);
- int32_t pid = data.readInt32();
- const int32_t type = data.readInt32();
- notifyResourceGranted(/*in*/ pid, /*in*/ type);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------
-
-} // namespace android
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index cca8f81..bd974b0 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -68,7 +68,7 @@
// TODO: Reimplemement based on standard C++ container?
};
-static sp<HeapCache> gHeapCache = new HeapCache();
+static sp<HeapCache> gHeapCache = sp<HeapCache>::make();
/******************************************************************************/
@@ -288,7 +288,7 @@
int32_t heapId = mHeapId.load(memory_order_acquire);
if (heapId == -1) {
sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
- sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
+ sp<BpMemoryHeap> heap = sp<BpMemoryHeap>::cast(find_heap(binder));
heap->assertReallyMapped();
if (heap->mBase != MAP_FAILED) {
Mutex::Autolock _l(mLock);
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/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index 570edb9..d26754e 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/IProcessInfoService.h>
+#include <processinfo/IProcessInfoService.h>
#include <binder/Parcel.h>
#include <utils/Errors.h>
#include <sys/types.h>
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index ca067e2..61f4581 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -102,7 +102,7 @@
}
}
- gDefaultServiceManager = new ServiceManagerShim(sm);
+ gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);
});
return gDefaultServiceManager;
@@ -324,7 +324,7 @@
}
if (out != nullptr) return out;
- sp<Waiter> waiter = new Waiter;
+ sp<Waiter> waiter = sp<Waiter>::make();
if (!mTheRealServiceManager->registerForNotifications(
name, waiter).isOk()) {
return nullptr;
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
deleted file mode 100644
index 4edc493..0000000
--- a/libs/binder/IpPrefix.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "IpPrefix"
-
-#include <binder/IpPrefix.h>
-#include <vector>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-
-using android::BAD_VALUE;
-using android::NO_ERROR;
-using android::Parcel;
-using android::status_t;
-
-namespace android {
-
-namespace net {
-
-#define RETURN_IF_FAILED(calledOnce) \
- { \
- status_t returnStatus = calledOnce; \
- if (returnStatus) { \
- ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
- return returnStatus; \
- } \
- }
-
-status_t IpPrefix::writeToParcel(Parcel* parcel) const {
- /*
- * Keep implementation in sync with writeToParcel() in
- * frameworks/base/core/java/android/net/IpPrefix.java.
- */
- std::vector<uint8_t> byte_vector;
-
- if (mIsIpv6) {
- const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
- byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
- } else {
- const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
- byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
- }
-
- RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
- RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
-
- return NO_ERROR;
-}
-
-status_t IpPrefix::readFromParcel(const Parcel* parcel) {
- /*
- * Keep implementation in sync with readFromParcel() in
- * frameworks/base/core/java/android/net/IpPrefix.java.
- */
- std::vector<uint8_t> byte_vector;
-
- RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
- RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
-
- if (byte_vector.size() == 16) {
- mIsIpv6 = true;
- memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
-
- } else if (byte_vector.size() == 4) {
- mIsIpv6 = false;
- memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
-
- } else {
- ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
- return BAD_VALUE;
- }
-
- return NO_ERROR;
-}
-
-const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
-{
- return mUnion.mIn6Addr;
-}
-
-const struct in_addr& IpPrefix::getAddressAsInAddr() const
-{
- return mUnion.mInAddr;
-}
-
-bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
-{
- if (isIpv6()) {
- *addr = mUnion.mIn6Addr;
- return true;
- }
- return false;
-}
-
-bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
-{
- if (isIpv4()) {
- *addr = mUnion.mInAddr;
- return true;
- }
- return false;
-}
-
-bool IpPrefix::isIpv6() const
-{
- return mIsIpv6;
-}
-
-bool IpPrefix::isIpv4() const
-{
- return !mIsIpv6;
-}
-
-int32_t IpPrefix::getPrefixLength() const
-{
- return mPrefixLength;
-}
-
-void IpPrefix::setAddress(const struct in6_addr& addr)
-{
- mUnion.mIn6Addr = addr;
- mIsIpv6 = true;
-}
-
-void IpPrefix::setAddress(const struct in_addr& addr)
-{
- mUnion.mInAddr = addr;
- mIsIpv6 = false;
-}
-
-void IpPrefix::setPrefixLength(int32_t prefix)
-{
- mPrefixLength = prefix;
-}
-
-bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
-{
- if (lhs.mIsIpv6 != rhs.mIsIpv6) {
- return false;
- }
-
- if (lhs.mPrefixLength != rhs.mPrefixLength) {
- return false;
- }
-
- if (lhs.mIsIpv6) {
- return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
- }
-
- return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
-}
-
-} // namespace net
-
-} // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index f96b6bb..b503beb 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -129,7 +129,9 @@
}
if (!reRegister) {
- if(!manager->registerClientCallback(name, service, this).isOk()) {
+ if (!manager->registerClientCallback(name, service,
+ sp<android::os::IClientCallback>::fromExisting(this))
+ .isOk()) {
ALOGE("Failed to add client callback for service %s", name.c_str());
return false;
}
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index b46b3e8..c4475c7 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -228,10 +228,8 @@
// ----------------------------------------------------------------------------
MemoryDealer::MemoryDealer(size_t size, const char* name, uint32_t flags)
- : mHeap(new MemoryHeapBase(size, flags, name)),
- mAllocator(new SimpleBestFitAllocator(size))
-{
-}
+ : mHeap(sp<MemoryHeapBase>::make(size, flags, name)),
+ mAllocator(new SimpleBestFitAllocator(size)) {}
MemoryDealer::~MemoryDealer()
{
@@ -243,7 +241,7 @@
sp<IMemory> memory;
const ssize_t offset = allocator()->allocate(size);
if (offset >= 0) {
- memory = new Allocation(this, heap(), offset, size);
+ memory = sp<Allocation>::make(sp<MemoryDealer>::fromExisting(this), heap(), offset, size);
}
return memory;
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 3773760..a735309 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,12 +268,33 @@
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) {
switch (flat->hdr.type) {
case BINDER_TYPE_BINDER: {
- sp<IBinder> binder = reinterpret_cast<IBinder*>(flat->cookie);
+ sp<IBinder> binder =
+ sp<IBinder>::fromExisting(reinterpret_cast<IBinder*>(flat->cookie));
return finishUnflattenBinder(binder, out);
}
case BINDER_TYPE_HANDLE: {
@@ -375,6 +419,11 @@
status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
{
+ if (parcel->isForRpc() != isForRpc()) {
+ ALOGE("Cannot append Parcel of one format to another.");
+ return BAD_TYPE;
+ }
+
status_t err;
const uint8_t *data = parcel->mData;
const binder_size_t *objects = parcel->mObjects;
@@ -511,6 +560,26 @@
mDeallocZero = true;
}
+void Parcel::markForBinder(const sp<IBinder>& binder) {
+ LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
+
+ if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
+ markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
+ }
+}
+
+void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+ LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
+ "format must be set before data is written OR on IPC data");
+
+ 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 +602,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 +656,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 +1144,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;
@@ -2032,6 +2111,9 @@
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount, release_func relFunc)
{
+ // this code uses 'mOwner == nullptr' to understand whether it owns memory
+ LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
+
freeData();
mData = const_cast<uint8_t*>(data);
@@ -2413,6 +2495,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/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index f75141e..0fb954a 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/ProcessInfoService.h>
+#include <processinfo/ProcessInfoService.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index edadcd5..7647a8c 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -105,7 +105,7 @@
}
std::lock_guard<std::mutex> l(gProcessMutex);
- gProcess = new ProcessState(driver);
+ gProcess = sp<ProcessState>::make(driver);
});
if (requireDefault) {
@@ -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);
@@ -297,8 +299,8 @@
return nullptr;
}
- b = BpBinder::create(handle);
- e->binder = b;
+ sp<BpBinder> b = BpBinder::create(handle);
+ e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
@@ -338,7 +340,7 @@
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
- sp<Thread> t = new PoolThread(isMain);
+ sp<Thread> t = sp<PoolThread>::make(isMain);
t->run(name.string());
}
}
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..1bf3d88
--- /dev/null
+++ b/libs/binder/RpcConnection.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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 <utils/String8.h>
+
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#ifdef __GLIBC__
+extern "C" pid_t gettid();
+#endif
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcConnection::SocketAddress::~SocketAddress() {}
+
+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 sp<RpcConnection>::make();
+}
+
+class UnixSocketAddress : public RpcConnection::SocketAddress {
+public:
+ explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "%u %s", pathLen, path);
+ memcpy(mAddr.sun_path, path, pathLen);
+ }
+ virtual ~UnixSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+ mAddr.sun_path)
+ .c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_un mAddr;
+};
+
+bool RpcConnection::setupUnixDomainServer(const char* path) {
+ return addServer(UnixSocketAddress(path));
+}
+
+bool RpcConnection::addUnixDomainClient(const char* path) {
+ return addClient(UnixSocketAddress(path));
+}
+
+#ifdef __BIONIC__
+
+class VsockSocketAddress : public RpcConnection::SocketAddress {
+public:
+ VsockSocketAddress(unsigned int cid, unsigned int port)
+ : mAddr({
+ .svm_family = AF_VSOCK,
+ .svm_port = port,
+ .svm_cid = cid,
+ }) {}
+ virtual ~VsockSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("cid %du port %du", mAddr.svm_cid, mAddr.svm_port).c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_vm mAddr;
+};
+
+bool RpcConnection::setupVsockServer(unsigned int port) {
+ // realizing value w/ this type at compile time to avoid ubsan abort
+ constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+ return addServer(VsockSocketAddress(kAnyCid, port));
+}
+
+bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
+ return addClient(VsockSocketAddress(cid, port));
+}
+
+#endif // __BIONIC__
+
+sp<IBinder> RpcConnection::getRootObject() {
+ ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
+ return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this));
+}
+
+status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this),
+ (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
+ : SocketUse::CLIENT);
+ return state()->transact(socket.fd(), address, code, data,
+ sp<RpcConnection>::fromExisting(this), reply, flags);
+}
+
+status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
+ ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT_REFCOUNT);
+ return state()->sendDecStrong(socket.fd(), address);
+}
+
+void RpcConnection::join() {
+ // establish a connection
+ {
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, 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());
+
+ assignServerToThisThread(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(sp<RpcConnection>::fromExisting(this), SocketUse::SERVER);
+
+ while (true) {
+ status_t error =
+ state()->getAndExecuteCommand(socket.fd(), sp<RpcConnection>::fromExisting(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;
+}
+
+bool RpcConnection::addServer(const SocketAddress& addr) {
+ LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ int savedErrno = errno;
+ ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ mServer = std::move(serverFd);
+ return true;
+}
+
+bool RpcConnection::addClient(const SocketAddress& addr) {
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
+ connection->fd = std::move(serverFd);
+ mClients.push_back(connection);
+ return true;
+}
+
+void RpcConnection::assignServerToThisThread(base::unique_fd&& fd) {
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
+ 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..1fa37ba
--- /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 sp<RpcServer>::make();
+}
+
+void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
+ mAgreedExperimental = true;
+}
+
+sp<RpcConnection> RpcServer::addClientConnection() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+
+ auto connection = RpcConnection::make();
+ connection->setForServer(sp<RpcServer>::fromExisting(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..755ff35
--- /dev/null
+++ b/libs/binder/RpcState.cpp
@@ -0,0 +1,677 @@
+/*
+ * 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, MSG_NOSIGNAL));
+
+ 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 | MSG_NOSIGNAL));
+
+ 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_reply_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_reply_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));
+}
+
+static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount) {
+ (void)p;
+ (void)data;
+ (void)dataSize;
+ (void)objects;
+ (void)objectsCount;
+}
+
+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;
+ // transaction->data is owned by this function. Parcel borrows this data and
+ // only holds onto it for the duration of this function call. Parcel will be
+ // deleted before the 'transactionData' object.
+ data.ipcSetDataReference(transaction->data,
+ transactionData.size() - offsetof(RpcWireTransaction, data),
+ nullptr /*object*/, 0 /*objectCount*/, do_nothing_to_transact_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..f12ef4e 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -38,6 +38,30 @@
};
}
+void Stability::forceDowngradeToStability(const sp<IBinder>& binder, Level level) {
+ // 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(level);
+ status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
+}
+
+void Stability::forceDowngradeToLocalStability(const sp<IBinder>& binder) {
+ forceDowngradeToStability(binder, getLocalLevel());
+}
+
+void Stability::forceDowngradeToSystemStability(const sp<IBinder>& binder) {
+ forceDowngradeToStability(binder, Level::SYSTEM);
+}
+
+void Stability::forceDowngradeToVendorStability(const sp<IBinder>& binder) {
+ forceDowngradeToStability(binder, Level::VENDOR);
+}
+
std::string Stability::Category::debugString() {
return levelString(level) + " wire protocol version "
+ std::to_string(version);
@@ -45,13 +69,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 +86,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 +96,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 +112,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 +150,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 +163,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/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index eac1bb2..c048cbe 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -143,7 +143,10 @@
OP_COARSE_LOCATION_SOURCE = 109,
OP_MANAGE_MEDIA = 110,
OP_BLUETOOTH_CONNECT = 111,
- _NUM_OP = 112
+ OP_UWB_RANGING = 112,
+ OP_ACTIVITY_RECOGNITION_SOURCE = 113,
+ OP_BLUETOOTH_ADVERTISE = 114,
+ _NUM_OP = 115
};
AppOpsManager();
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 7079544..7e9be41 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -131,8 +131,8 @@
virtual void onLastStrongRef(const void* id);
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
- inline IBinder* remote() { return mRemote; }
- inline IBinder* remote() const { return mRemote; }
+ inline IBinder* remote() const { return mRemote; }
+ inline sp<IBinder> remoteStrong() const { return sp<IBinder>::fromExisting(mRemote); }
private:
BpRefBase(const BpRefBase& o);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 22300ac..ad618f9 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;
}
@@ -36,7 +40,15 @@
class BpBinder : public IBinder
{
public:
- static BpBinder* create(int32_t handle);
+ static sp<BpBinder> create(int32_t handle);
+ static sp<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,57 @@
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;
+ friend class sp<BpBinder>;
- 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/IInterface.h b/libs/binder/include/binder/IInterface.h
index f930d29..ff90b30 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -143,11 +143,10 @@
{ \
::android::sp<I##INTERFACE> intr; \
if (obj != nullptr) { \
- intr = static_cast<I##INTERFACE*>( \
- obj->queryLocalInterface( \
- I##INTERFACE::descriptor).get()); \
+ intr = ::android::sp<I##INTERFACE>::cast( \
+ obj->queryLocalInterface(I##INTERFACE::descriptor)); \
if (intr == nullptr) { \
- intr = new Bp##INTERFACE(obj); \
+ intr = ::android::sp<Bp##INTERFACE>::make(obj); \
} \
} \
return intr; \
@@ -186,7 +185,7 @@
inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
const String16& _descriptor)
{
- if (_descriptor == INTERFACE::descriptor) return this;
+ if (_descriptor == INTERFACE::descriptor) return sp<IInterface>::fromExisting(this);
return nullptr;
}
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
deleted file mode 100644
index f92d557..0000000
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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
-
-#ifndef __ANDROID_VNDK__
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IMediaResourceMonitor : public IInterface {
-public:
- DECLARE_META_INTERFACE(MediaResourceMonitor)
-
- // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
- enum {
- TYPE_VIDEO_CODEC = 0,
- TYPE_AUDIO_CODEC = 1,
- };
-
- virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
-
- enum {
- NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
-public:
- // NOLINTNEXTLINE(google-default-arguments)
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
deleted file mode 100644
index a8faa3f..0000000
--- a/libs/binder/include/binder/IpPrefix.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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
-
-#ifndef __ANDROID_VNDK__
-
-#include <netinet/in.h>
-
-#include <binder/Parcelable.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-namespace net {
-
-/*
- * C++ implementation of the Java class android.net.IpPrefix
- */
-class IpPrefix : public Parcelable {
-public:
- IpPrefix() = default;
- virtual ~IpPrefix() = default;
- IpPrefix(const IpPrefix& prefix) = default;
-
- IpPrefix(const struct in6_addr& addr, int32_t plen):
- mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
-
- IpPrefix(const struct in_addr& addr, int32_t plen):
- mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
-
- bool getAddressAsIn6Addr(struct in6_addr* addr) const;
- bool getAddressAsInAddr(struct in_addr* addr) const;
-
- const struct in6_addr& getAddressAsIn6Addr() const;
- const struct in_addr& getAddressAsInAddr() const;
-
- bool isIpv6() const;
- bool isIpv4() const;
-
- int32_t getPrefixLength() const;
-
- void setAddress(const struct in6_addr& addr);
- void setAddress(const struct in_addr& addr);
-
- void setPrefixLength(int32_t prefix);
-
- friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
-
- friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
- return !(lhs == rhs);
- }
-
-public:
- // Overrides
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
-private:
- union InternalUnion {
- InternalUnion() = default;
- explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }
- explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }
- struct in6_addr mIn6Addr;
- struct in_addr mInAddr;
- } mUnion;
- int32_t mPrefixLength;
- bool mIsIpv6;
-};
-
-} // namespace net
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 7b298f5..211790d 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,17 @@
// 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);
+
+ // 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);
@@ -523,6 +536,10 @@
const binder_size_t* objects, size_t objectsCount,
release_func relFunc);
+ // Whenever possible, markForBinder should be preferred. This method is
+ // called automatically on reply Parcels for RPC transactions.
+ void markForRpc(const sp<RpcConnection>& connection);
+
status_t finishWrite(size_t len);
void releaseObjects();
void acquireObjects();
@@ -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/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 2405ab6..0919648 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -88,7 +88,8 @@
static sp<ProcessState> init(const char *defaultDriver, bool requireDefault);
friend class IPCThreadState;
-
+ friend class sp<ProcessState>;
+
explicit ProcessState(const char* driver);
~ProcessState();
@@ -124,7 +125,6 @@
Vector<handle_entry>mHandleToObject;
- String8 mRootDir;
bool mThreadPoolStarted;
volatile int32_t mThreadPoolSeq;
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..efa922d
--- /dev/null
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -0,0 +1,180 @@
+/*
+ * 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);
+
+#ifdef __BIONIC__
+ /**
+ * Creates an RPC server at the current port.
+ */
+ [[nodiscard]] bool setupVsockServer(unsigned int port);
+
+ /**
+ * Connects to an RPC server at the CVD & port.
+ */
+ [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
+#endif // __BIONIC__
+
+ /**
+ * 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; }
+
+ class SocketAddress {
+ public:
+ virtual ~SocketAddress();
+ virtual std::string toString() const = 0;
+ virtual const sockaddr* addr() const = 0;
+ virtual size_t addrSize() const = 0;
+ };
+
+private:
+ friend sp<RpcConnection>;
+ RpcConnection();
+
+ bool addServer(const SocketAddress& address);
+ bool addClient(const SocketAddress& address);
+ void assignServerToThisThread(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..d29b651
--- /dev/null
+++ b/libs/binder/include/binder/RpcServer.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
+
+#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:
+ friend sp<RpcServer>;
+ 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..f4bfac8 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -49,10 +49,42 @@
// 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 the local partition.
+ static void forceDowngradeToLocalStability(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: The only client of
+ // - forceDowngradeToSystemStability() and;
+ // - korceDowngradeToVendorStability()
+ // should be AIBinder_forceDowngradeToLocalStability().
+ //
+ // getLocalLevel() in libbinder returns Level::SYSTEM when called
+ // from libbinder_ndk (even on vendor partition). So we explicitly provide
+ // these methods for use by the NDK API:
+ // AIBinder_forceDowngradeToLocalStability().
+ //
+ // This allows correctly downgrading the binder's stability to either system/vendor,
+ // depending on the partition.
+
+ // 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 the vendor partition.
+ static void forceDowngradeToVendorStability(const sp<IBinder>& binder);
+
+ // 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 the system partition.
+ static void forceDowngradeToSystemStability(const sp<IBinder>& binder);
+
// 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 +171,17 @@
// returns the stability according to how this was built
static Level getLocalLevel();
+ // Downgrades binder stability to the specified level.
+ static void forceDowngradeToStability(const sp<IBinder>& binder, Level level);
+
+ 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/include/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
similarity index 93%
rename from libs/binder/include/binder/ActivityManager.h
rename to libs/binder/include_activitymanager/binder/ActivityManager.h
index 830971b..b772b80 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -41,7 +41,11 @@
// Flag for registerUidObserver: report uid has become idle
UID_OBSERVER_IDLE = 1<<2,
// Flag for registerUidObserver: report uid has become active
- UID_OBSERVER_ACTIVE = 1<<3
+ UID_OBSERVER_ACTIVE = 1<<3,
+ // Flag for registerUidObserver: report uid cached state has changed
+ UID_OBSERVER_CACHED = 1<<4,
+ // Flag for registerUidObserver: report uid capability has changed
+ UID_OBSERVER_CAPABILITY = 1<<5,
};
// PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl.
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
similarity index 99%
rename from libs/binder/include/binder/IActivityManager.h
rename to libs/binder/include_activitymanager/binder/IActivityManager.h
index 2d58c46..4632b2e 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -18,8 +18,8 @@
#ifndef __ANDROID_VNDK__
-#include <binder/IInterface.h>
#include <binder/IUidObserver.h>
+#include <binder/IInterface.h>
namespace android {
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include_activitymanager/binder/IUidObserver.h
similarity index 100%
rename from libs/binder/include/binder/IUidObserver.h
rename to libs/binder/include_activitymanager/binder/IUidObserver.h
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include_batterystats/batterystats/IBatteryStats.h
similarity index 100%
rename from libs/binder/include/binder/IBatteryStats.h
rename to libs/binder/include_batterystats/batterystats/IBatteryStats.h
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include_processinfo/processinfo/IProcessInfoService.h
similarity index 100%
rename from libs/binder/include/binder/IProcessInfoService.h
rename to libs/binder/include_processinfo/processinfo/IProcessInfoService.h
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include_processinfo/processinfo/ProcessInfoService.h
similarity index 98%
rename from libs/binder/include/binder/ProcessInfoService.h
rename to libs/binder/include_processinfo/processinfo/ProcessInfoService.h
index 6b3b5ce..978856d 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include_processinfo/processinfo/ProcessInfoService.h
@@ -18,7 +18,7 @@
#ifndef __ANDROID_VNDK__
-#include <binder/IProcessInfoService.h>
+#include <processinfo/IProcessInfoService.h>
#include <utils/Errors.h>
#include <utils/Singleton.h>
#include <sys/types.h>
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 3c90681..1dcb41b 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -363,7 +363,8 @@
}
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
- CHECK(who == mWho);
+ CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
+ << " (" << mWho.get_refs() << ")";
mOnDied(mCookie);
@@ -598,6 +599,8 @@
}
*in = new AParcel(binder);
+ (*in)->get()->markForBinder(binder->getBinder());
+
status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
binder_status_t ret = PruneStatusT(status);
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index a44c261..b9adc9a 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -36,11 +36,9 @@
__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
-
+/**
+ * Flags for AIBinder_transact.
+ */
typedef uint32_t binder_flags_t;
enum {
/**
@@ -52,7 +50,10 @@
FLAG_ONEWAY = 0x01,
};
-// Also see IBinder.h in libbinder
+/**
+ * Codes for AIBinder_transact. This defines the range of codes available for
+ * usage. Other codes are used or reserved by the Android system.
+ */
typedef uint32_t transaction_code_t;
enum {
/**
@@ -207,7 +208,8 @@
*
* Available since API level 29.
*
- * \param dump function to call when an instance of this binder class is being dumped.
+ * \param clazz class which should use this dump function
+ * \param onDump function to call when an instance of this binder class is being dumped.
*/
void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 05b25e7..6f1fdfc 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,11 +184,12 @@
/**
* 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.
*
- * \param a low-level error to associate with this status object.
+ * \param status a low-level error to associate with this status object.
*
* \return a newly constructed status object that the caller owns.
*/
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 5df0012..5516914 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, do not use with unstable
+ * 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, do not use with unstable
+ * 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, do not use with unstable
+ * 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, do not use with unstable
+ * 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/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 44ed48f..f113ba8 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -30,9 +30,7 @@
FLAG_PRIVATE_VENDOR = 0x10000000,
};
-// TODO(b/180646847): __ANDROID_APEX__ here is what allows product partition to
-// talk to system.
-#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID_VENDOR__)
enum {
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
@@ -47,7 +45,19 @@
AIBinder_markVendorStability(binder);
}
-#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+/**
+ * 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 the vendor partition.
+ */
+void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
+
+static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) {
+ AIBinder_forceDowngradeToVendorStability(binder);
+}
+
+#else // defined(__ANDROID_VENDOR__)
enum {
FLAG_PRIVATE_LOCAL = 0,
@@ -64,9 +74,27 @@
AIBinder_markSystemStability(binder);
}
-#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+/**
+ * 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 the system partition.
+ */
+void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
+
+static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) {
+ AIBinder_forceDowngradeToSystemStability(binder);
+}
+
+#endif // defined(__ANDROID_VENDOR__)
/**
+ * WARNING: this is not expected to be used manually. When the build system has
+ * versioned checks in place for an interface that prevent it being changed year
+ * over year (specifically like those for @VintfStability stable AIDL
+ * interfaces), this could be called. Calling this without this or equivalent
+ * infrastructure will lead to de facto frozen APIs or GSI test failures.
+ *
* This interface has system<->vendor stability
*/
void AIBinder_markVintfStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 8d08275..67c85b6 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
@@ -126,6 +127,9 @@
AServiceManager_tryUnregister; # llndk
AServiceManager_reRegister; # llndk
+ AIBinder_forceDowngradeToSystemStability; # apex
+ AIBinder_forceDowngradeToVendorStability; # llndk
+
AIBinder_Class_getDescriptor;
AIBinder_Weak_clone;
AIBinder_Weak_lt;
@@ -139,7 +143,6 @@
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
- AParcel_markSensitive;
extern "C++" {
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
diff --git a/libs/binder/ndk/parcel_internal.h b/libs/binder/ndk/parcel_internal.h
index 6b7295e..b4f6358 100644
--- a/libs/binder/ndk/parcel_internal.h
+++ b/libs/binder/ndk/parcel_internal.h
@@ -27,9 +27,8 @@
const ::android::Parcel* get() const { return mParcel; }
::android::Parcel* get() { return mParcel; }
- explicit AParcel(const AIBinder* binder)
- : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
- AParcel(const AIBinder* binder, ::android::Parcel* parcel, bool owns)
+ explicit AParcel(AIBinder* binder) : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
+ AParcel(AIBinder* binder, ::android::Parcel* parcel, bool owns)
: mBinder(binder), mParcel(parcel), mOwns(owns) {}
~AParcel() {
@@ -38,7 +37,7 @@
}
}
- static const AParcel readOnly(const AIBinder* binder, const ::android::Parcel* parcel) {
+ static const AParcel readOnly(AIBinder* binder, const ::android::Parcel* parcel) {
return AParcel(binder, const_cast<::android::Parcel*>(parcel), false);
}
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
index a5b3ece..7eafb9c 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -31,7 +31,7 @@
#error libbinder_ndk should only be built in a system context
#endif
-// explicit extern because symbol is only declared in header when __ANDROID_VNDK__
+// explicit extern because symbol is only declared in header when __ANDROID_VENDOR__
extern "C" void AIBinder_markVendorStability(AIBinder* binder) {
Stability::markVndk(binder->getBinder().get());
}
@@ -43,3 +43,12 @@
void AIBinder_markVintfStability(AIBinder* binder) {
Stability::markVintf(binder->getBinder().get());
}
+
+// explicit extern because symbol is only declared in header when __ANDROID_VENDOR__
+extern "C" void AIBinder_forceDowngradeToVendorStability(AIBinder* binder) {
+ Stability::forceDowngradeToVendorStability(binder->getBinder());
+}
+
+void AIBinder_forceDowngradeToSystemStability(AIBinder* binder) {
+ Stability::forceDowngradeToSystemStability(binder->getBinder());
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 3899d47..321b422 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -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
@@ -94,6 +114,8 @@
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.
@@ -602,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,
}
}
};
@@ -616,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,
}
}
};
@@ -635,6 +695,8 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+
+ stability: $stability:expr,
}
} => {
#[doc = $proxy_doc]
@@ -669,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/lib.rs b/libs/binder/rust/src/lib.rs
index 5bbd2a3..30928a5 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,8 +107,9 @@
pub mod parcel;
pub use crate::binder::{
- FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, Strong,
- TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+ 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};
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/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/tests/Android.bp b/libs/binder/tests/Android.bp
index 3bbb0b5..afc4b1b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -104,6 +104,53 @@
require_root: true,
}
+aidl_interface {
+ name: "binderRpcTestIface",
+ host_supported: true,
+ unstable: true,
+ srcs: [
+ "IBinderRpcSession.aidl",
+ "IBinderRpcTest.aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
+}
+
+cc_test {
+ name: "binderRpcTest",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ defaults: [
+ "binder_test_defaults",
+ "libbinder_ndk_host_user",
+ ],
+
+ srcs: [
+ "binderRpcTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libbase",
+ "libutils",
+ "libcutils",
+ "liblog",
+ ],
+ static_libs: [
+ "binderRpcTestIface-cpp",
+ "binderRpcTestIface-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
+
cc_test {
name: "binderThroughputTest",
defaults: ["binder_test_defaults"],
@@ -190,6 +237,11 @@
srcs: [
"IBinderStabilityTest.aidl",
],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
}
cc_test {
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..a51c987
--- /dev/null
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -0,0 +1,874 @@
+/*
+ * 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 <BnBinderRpcSession.h>
+#include <BnBinderRpcTest.h>
+#include <aidl/IBinderRpcTest.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.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 <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif //__BIONIC__
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "../RpcState.h" // for debugging
+
+namespace android {
+
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+ Parcel p;
+ p.writeInt32(3);
+
+ EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
+}
+
+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;
+ static bool gUseTmp = access("/tmp/", F_OK) != -1;
+
+ if (gUseTmp) {
+ return "/tmp/binderRpcTest_" + std::to_string(id++);
+ } else {
+ 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";
+ }
+};
+
+// 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;
+ }
+};
+
+enum class SocketType {
+ UNIX,
+#ifdef __BIONIC__
+ VSOCK,
+#endif // __BIONIC__
+};
+static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
+ switch (info.param) {
+ case SocketType::UNIX:
+ return "unix_domain_socket";
+#ifdef __BIONIC__
+ case SocketType::VSOCK:
+ return "vm_socket";
+#endif // __BIONIC__
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ return "";
+ }
+}
+class BinderRpc : public ::testing::TestWithParam<SocketType> {
+public:
+ // 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);
+
+ SocketType socketType = GetParam();
+
+ std::string addr = allocateSocketAddress();
+ unlink(addr.c_str());
+ static unsigned int port = 3456;
+ port++;
+
+ auto ret = ProcessConnection{
+ .host = Process([&] {
+ sp<RpcServer> server = RpcServer::make();
+
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+ // server supporting one client on one socket
+ sp<RpcConnection> connection = server->addClientConnection();
+
+ switch (socketType) {
+ case SocketType::UNIX:
+ CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+ break;
+#ifdef __BIONIC__
+ case SocketType::VSOCK:
+ CHECK(connection->setupVsockServer(port));
+ break;
+#endif // __BIONIC__
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+
+ 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(),
+ };
+
+ // create remainder of connections
+ for (size_t i = 0; i < numThreads; i++) {
+ for (size_t tries = 0; tries < 5; tries++) {
+ usleep(10000);
+ switch (socketType) {
+ case SocketType::UNIX:
+ if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
+ break;
+#ifdef __BIONIC__
+ case SocketType::VSOCK:
+ if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
+ break;
+#endif // __BIONIC__
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+ }
+ LOG_ALWAYS_FATAL("Could not connect");
+ success:;
+ }
+
+ ret.rootBinder = ret.connection->getRootObject();
+ return ret;
+ }
+
+ 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_P(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_P(BinderRpc, Ping) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+TEST_P(BinderRpc, GetInterfaceDescriptor) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
+}
+
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, AppendSeparateFormats) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ Parcel p1;
+ p1.markForBinder(proc.rootBinder);
+ p1.writeInt32(3);
+
+ Parcel p2;
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
+ EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
+}
+
+TEST_P(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_P(BinderRpc, SendSomethingOneway) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ EXPECT_OK(proc.rootIface->sendString("asdf"));
+}
+
+TEST_P(BinderRpc, SendAndGetResultBack) {
+ auto proc = createRpcTestSocketServerProcess(1);
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+TEST_P(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_P(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_P(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_P(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_P(BinderRpc, RepeatBinderNull) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
+ EXPECT_EQ(nullptr, outBinder);
+}
+
+TEST_P(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_P(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_P(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_P(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_P(BinderRpc, RepeatRootObject) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
+ EXPECT_EQ(proc.rootBinder, outBinder);
+}
+
+TEST_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(BinderRpc, Die) {
+ 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;
+ }
+}
+
+TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
+ ASSERT_NE(binder, nullptr);
+
+ ASSERT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
+ ASSERT_NE(binder, nullptr);
+
+ auto ndkBinder = aidl::IBinderRpcTest::fromBinder(binder);
+ ASSERT_NE(ndkBinder, nullptr);
+
+ std::string out;
+ ndk::ScopedAStatus status = ndkBinder->doubleString("aoeu", &out);
+ ASSERT_TRUE(status.isOk()) << status.getDescription();
+ ASSERT_EQ("aoeuaoeu", out);
+}
+
+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_P(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?");
+}
+
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
+ ::testing::Values(SocketType::UNIX
+#ifdef __BIONIC__
+ ,
+ SocketType::VSOCK
+#endif // __BIONIC__
+ ),
+ PrintSocketType);
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 1f2779a..cb309bd 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_stability.h>
#include <binder/Binder.h>
@@ -131,6 +132,55 @@
EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf()));
}
+TEST(BinderStability, ForceDowngradeToLocalStability) {
+ 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::forceDowngradeToLocalStability(someBinder);
+
+ EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
+}
+
+TEST(BinderStability, NdkForceDowngradeToLocalStability) {
+ 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
+ AIBinder_forceDowngradeToLocalStability(AIBinder_fromPlatformBinder(someBinder));
+
+ EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
+}
+
+TEST(BinderStability, ForceDowngradeToVendorStability) {
+ sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
+ auto server = interface_cast<IBinderStabilityTest>(serverBinder);
+
+ ASSERT_NE(nullptr, server.get());
+ ASSERT_NE(nullptr, IInterface::asBinder(server)->remoteBinder());
+
+ {
+ sp<BadStableBinder> binder = BadStableBinder::vintf();
+
+ EXPECT_TRUE(Stability::requiresVintfDeclaration(binder));
+ EXPECT_TRUE(server->sendAndCallBinder(binder).isOk());
+ EXPECT_TRUE(binder->gotUserTransaction);
+ }
+ {
+ sp<BadStableBinder> binder = BadStableBinder::vintf();
+
+ // This method should never be called directly. This is done only for the test.
+ Stability::forceDowngradeToVendorStability(binder);
+
+ // Binder downgraded to vendor stability, cannot be called from system context
+ EXPECT_FALSE(Stability::requiresVintfDeclaration(binder));
+ EXPECT_EQ(BAD_TYPE, server->sendAndCallBinder(binder).exceptionCode());
+ EXPECT_FALSE(binder->gotUserTransaction);
+ }
+}
+
TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
sp<IBinder> vintfServer = BadStableBinder::vintf();
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index ff059d7..64203f7 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -39,12 +39,50 @@
min_sdk_version: "29",
}
+cc_library_headers {
+ name: "libgui_aidl_headers",
+ vendor_available: true,
+ static_libs: [
+ "libgui_aidl_static",
+ ],
+
+ export_static_lib_headers: [
+ "libgui_aidl_static",
+ ],
+}
+
filegroup {
name: "libgui_aidl",
srcs: ["aidl/**/*.aidl"],
path: "aidl/",
}
+cc_library_static {
+ name: "libgui_aidl_static",
+ vendor_available: true,
+ srcs: [
+ ":libgui_aidl",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libui",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "libbinder",
+ ],
+
+ aidl: {
+ export_aidl_headers: true
+ }
+}
+
+
cc_library_shared {
name: "libgui",
vendor_available: true,
@@ -56,10 +94,16 @@
defaults: ["libgui_bufferqueue-defaults"],
+ static_libs: [
+ "libgui_aidl_static",
+ ],
+ export_static_lib_headers: [
+ "libgui_aidl_static",
+ ],
+
srcs: [
":framework_native_aidl",
":inputconstants_aidl",
- ":libgui_aidl",
":libgui_bufferqueue_sources",
"BitTube.cpp",
@@ -115,6 +159,10 @@
"libinput",
],
+ export_header_lib_headers: [
+ "libgui_aidl_headers",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
@@ -136,15 +184,16 @@
},
},
- header_libs: [
- "libdvr_headers",
- "libpdx_headers",
- ],
-
aidl: {
export_aidl_headers: true,
},
+ header_libs: [
+ "libdvr_headers",
+ "libgui_aidl_headers",
+ "libpdx_headers",
+ ],
+
pgo: {
sampling: true,
profile_file: "libgui/libgui.profdata",
@@ -175,8 +224,8 @@
srcs: [
":inputconstants_aidl",
- ":libgui_aidl",
":libgui_bufferqueue_sources",
+ ":libgui_aidl",
],
}
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 8da97bb..e5afd40 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -204,13 +204,16 @@
if (mRequestedSize != newSize) {
mRequestedSize.set(newSize);
mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);
- if (mLastBufferScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+ if (mLastBufferInfo.scalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
// If the buffer supports scaling, update the frame immediately since the client may
// want to scale the existing buffer to the new size.
mSize = mRequestedSize;
- t.setFrame(mSurfaceControl,
- {0, 0, static_cast<int32_t>(mSize.width),
- static_cast<int32_t>(mSize.height)});
+ // We only need to update the scale if we've received at least one buffer. The reason
+ // for this is the scale is calculated based on the requested size and buffer size.
+ // If there's no buffer, the scale will always be 1.
+ if (mLastBufferInfo.hasBuffer) {
+ setMatrix(&t, mLastBufferInfo);
+ }
applyTransaction = true;
}
}
@@ -374,8 +377,10 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- mLastBufferScalingMode = bufferItem.mScalingMode;
mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
+ mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
+ bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
+ bufferItem.mScalingMode);
auto releaseBufferCallback =
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
@@ -388,8 +393,7 @@
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
- t->setFrame(mSurfaceControl,
- {0, 0, static_cast<int32_t>(mSize.width), static_cast<int32_t>(mSize.height)});
+ setMatrix(t, mLastBufferInfo);
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
@@ -515,6 +519,17 @@
return mSize != bufferSize;
}
+void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
+ const BufferInfo& bufferInfo) {
+ uint32_t bufWidth = bufferInfo.width;
+ uint32_t bufHeight = bufferInfo.height;
+
+ float dsdx = mSize.width / static_cast<float>(bufWidth);
+ float dsdy = mSize.height / static_cast<float>(bufHeight);
+
+ t->setMatrix(mSurfaceControl, dsdx, 0, 0, dsdy);
+}
+
void BLASTBufferQueue::setTransactionCompleteCallback(
uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
std::lock_guard _lock{mMutex};
@@ -555,11 +570,13 @@
}).detach();
}
- status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override {
- if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
+ status_t setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "BBQSurface::setFrameRate")) {
return BAD_VALUE;
}
- return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless);
+ return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
@@ -606,7 +623,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/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ceab6ec..454aa9e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -477,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());
@@ -962,6 +982,34 @@
return NO_ERROR;
}
+ status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, displayToken);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("addHdrLayerInfoListener: Failed to transact; error = %d", error);
+ }
+ return error;
+ }
+
+ status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, displayToken);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("removeHdrLayerInfoListener: Failed to transact; error = %d", error);
+ }
+ return error;
+ }
+
status_t notifyPowerBoost(int32_t boostId) override {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1013,39 +1061,15 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) override {
+ int8_t compatibility, int8_t changeFrameRateStrategy) override {
Parcel data, reply;
- status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(surface));
+ SAFE_PARCEL(data.writeFloat, frameRate);
+ SAFE_PARCEL(data.writeByte, compatibility);
+ SAFE_PARCEL(data.writeByte, changeFrameRateStrategy);
- err = data.writeStrongBinder(IInterface::asBinder(surface));
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeFloat(frameRate);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeByte(compatibility);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeBool(shouldBeSeamless);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
+ status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
return err;
@@ -1842,6 +1866,38 @@
}
return setDisplayBrightness(displayToken, brightness);
}
+ case ADD_HDR_LAYER_INFO_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("addHdrLayerInfoListener: Failed to read display token");
+ return error;
+ }
+ sp<gui::IHdrLayerInfoListener> listener;
+ error = data.readNullableStrongBinder(&listener);
+ if (error != NO_ERROR) {
+ ALOGE("addHdrLayerInfoListener: Failed to read listener");
+ return error;
+ }
+ return addHdrLayerInfoListener(displayToken, listener);
+ }
+ case REMOVE_HDR_LAYER_INFO_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("removeHdrLayerInfoListener: Failed to read display token");
+ return error;
+ }
+ sp<gui::IHdrLayerInfoListener> listener;
+ error = data.readNullableStrongBinder(&listener);
+ if (error != NO_ERROR) {
+ ALOGE("removeHdrLayerInfoListener: Failed to read listener");
+ return error;
+ }
+ return removeHdrLayerInfoListener(displayToken, listener);
+ }
case NOTIFY_POWER_BOOST: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
int32_t boostId;
@@ -1874,36 +1930,24 @@
case SET_FRAME_RATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
- status_t err = data.readStrongBinder(&binder);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.readStrongBinder, &binder);
+
sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
if (!surface) {
- ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
- strerror(-err), -err);
- return err;
+ ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer");
+ return BAD_VALUE;
}
float frameRate;
- err = data.readFloat(&frameRate);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.readFloat, &frameRate);
+
int8_t compatibility;
- err = data.readByte(&compatibility);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
- return err;
- }
- bool shouldBeSeamless;
- err = data.readBool(&shouldBeSeamless);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err);
- return err;
- }
- status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless);
+ SAFE_PARCEL(data.readByte, &compatibility);
+
+ int8_t changeFrameRateStrategy;
+ SAFE_PARCEL(data.readByte, &changeFrameRateStrategy);
+
+ status_t result =
+ setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy);
reply->writeInt32(result);
return NO_ERROR;
}
@@ -1963,6 +2007,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/LayerState.cpp b/libs/gui/LayerState.cpp
index d653ae7..8094385 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -19,6 +19,7 @@
#include <apex/window.h>
#include <inttypes.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposerClient.h>
@@ -60,7 +61,7 @@
frameRateSelectionPriority(-1),
frameRate(0.0f),
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
- shouldBeSeamless(true),
+ changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
fixedTransformHint(ui::Transform::ROT_INVALID),
frameNumber(0),
autoRefresh(false),
@@ -148,7 +149,7 @@
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
- SAFE_PARCEL(output.writeBool, shouldBeSeamless);
+ SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeUint64, frameNumber);
SAFE_PARCEL(output.writeBool, autoRefresh);
@@ -269,7 +270,7 @@
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
- SAFE_PARCEL(input.readBool, &shouldBeSeamless);
+ SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readUint64, &frameNumber);
@@ -429,10 +430,6 @@
barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
barrierFrameNumber = other.barrierFrameNumber;
}
- if (other.what & eReparentChildren) {
- what |= eReparentChildren;
- reparentSurfaceControl = other.reparentSurfaceControl;
- }
if (other.what & eRelativeLayerChanged) {
what |= eRelativeLayerChanged;
what &= ~eLayerChanged;
@@ -458,10 +455,6 @@
what |= eCropChanged;
crop = other.crop;
}
- if (other.what & eFrameChanged) {
- what |= eFrameChanged;
- orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
- }
if (other.what & eBufferChanged) {
what |= eBufferChanged;
buffer = other.buffer;
@@ -531,7 +524,7 @@
what |= eFrameRateChanged;
frameRate = other.frameRate;
frameRateCompatibility = other.frameRateCompatibility;
- shouldBeSeamless = other.shouldBeSeamless;
+ changeFrameRateStrategy = other.changeFrameRateStrategy;
}
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
@@ -563,6 +556,10 @@
}
}
+bool layer_state_t::hasBufferChanges() const {
+ return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+}
+
status_t layer_state_t::matrix22_t::write(Parcel& output) const {
SAFE_PARCEL(output.writeFloat, dsdx);
SAFE_PARCEL(output.writeFloat, dtdx);
@@ -624,8 +621,8 @@
return NO_ERROR;
}
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName,
- bool privileged) {
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+ const char* inFunctionName, bool privileged) {
const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
int floatClassification = std::fpclassify(frameRate);
if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
@@ -641,6 +638,12 @@
return false;
}
+ if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS &&
+ changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
+ ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
+ changeFrameRateStrategy);
+ }
+
return true;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6de3e97..2fc9d47 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1729,8 +1729,8 @@
int Surface::dispatchSetFrameRate(va_list args) {
float frameRate = static_cast<float>(va_arg(args, double));
int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
- bool shouldBeSeamless = static_cast<bool>(va_arg(args, int));
- return setFrameRate(frameRate, compatibility, shouldBeSeamless);
+ int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int));
+ return setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2575,16 +2575,18 @@
mSurfaceListener->onBuffersDiscarded(discardedBufs);
}
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
ATRACE_CALL();
ALOGV("Surface::setFrameRate");
- if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "Surface::setFrameRate")) {
return BAD_VALUE;
}
return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
- shouldBeSeamless);
+ changeFrameRateStrategy);
}
status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d7b2c2e..9ce094a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1157,20 +1157,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren(
- const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eReparentChildren;
- s->reparentSurfaceControl = newParent;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent(
const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
layer_state_t* s = getLayerState(sc);
@@ -1246,20 +1232,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
- const sp<SurfaceControl>& sc, const Rect& frame) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eFrameChanged;
- s->orientedDisplaySpaceRect = frame;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
ReleaseBufferCallback callback) {
@@ -1583,7 +1555,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
- bool shouldBeSeamless) {
+ int8_t changeFrameRateStrategy) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1591,7 +1563,8 @@
}
// Allow privileged values as well here, those will be ignored by SF if
// the caller is not privileged
- if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate",
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "Transaction::setFrameRate",
/*privileged=*/true)) {
mStatus = BAD_VALUE;
return *this;
@@ -1599,7 +1572,7 @@
s->what |= layer_state_t::eFrameRateChanged;
s->frameRate = frameRate;
s->frameRateCompatibility = compatibility;
- s->shouldBeSeamless = shouldBeSeamless;
+ s->changeFrameRateStrategy = changeFrameRateStrategy;
return *this;
}
@@ -1985,6 +1958,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,
@@ -2048,6 +2026,17 @@
return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
}
+status_t SurfaceComposerClient::addHdrLayerInfoListener(
+ const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+ return ComposerService::getComposerService()->addHdrLayerInfoListener(displayToken, listener);
+}
+
+status_t SurfaceComposerClient::removeHdrLayerInfoListener(
+ const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+ return ComposerService::getComposerService()->removeHdrLayerInfoListener(displayToken,
+ listener);
+}
+
status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
return ComposerService::getComposerService()->notifyPowerBoost(boostId);
}
diff --git a/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
new file mode 100644
index 0000000..fc809c4
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IHdrLayerInfoListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IHdrLayerInfoListener {
+ // Callback with the total number of HDR layers, the dimensions of the largest layer,
+ // and a placeholder flags
+ // TODO (b/182312559): Define the flags (likely need an indicator that a UDFPS layer is present)
+ void onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, int flags);
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index fbd16f4..a48f95a 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -142,6 +142,33 @@
ui::Size mRequestedSize GUARDED_BY(mMutex);
int32_t mFormat GUARDED_BY(mMutex);
+ struct BufferInfo {
+ bool hasBuffer = false;
+ uint32_t width;
+ uint32_t height;
+ uint32_t transform;
+ // This is used to check if we should update the blast layer size immediately or wait until
+ // we get the next buffer. This will support scenarios where the layer can change sizes
+ // and the buffer will scale to fit the new size.
+ uint32_t scalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+
+ void update(bool hasBuffer, uint32_t width, uint32_t height, uint32_t transform,
+ uint32_t scalingMode) {
+ this->hasBuffer = hasBuffer;
+ this->width = width;
+ this->height = height;
+ this->transform = transform;
+ this->scalingMode = scalingMode;
+ }
+ };
+
+ // Last acquired buffer's info. This is used to calculate the correct scale when size change is
+ // requested. We need to use the old buffer's info to determine what scale we need to apply to
+ // ensure the correct size.
+ BufferInfo mLastBufferInfo GUARDED_BY(mMutex);
+ void setMatrix(SurfaceComposerClient::Transaction* t, const BufferInfo& bufferInfo)
+ REQUIRES(mMutex);
+
uint32_t mTransformHint GUARDED_BY(mMutex);
sp<IGraphicBufferConsumer> mConsumer;
@@ -159,11 +186,6 @@
std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
- // Last acquired buffer's scaling mode. This is used to check if we should update the blast
- // layer size immediately or wait until we get the next buffer. This will support scenarios
- // where the layer can change sizes and the buffer will scale to fit the new size.
- uint32_t mLastBufferScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
-
// Tracks the last acquired frame number
uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h
index 3b4c009..a23c202 100644
--- a/libs/gui/include/gui/FrameTimelineInfo.h
+++ b/libs/gui/include/gui/FrameTimelineInfo.h
@@ -18,7 +18,6 @@
#include <stdint.h>
-#include <android/os/IInputConstants.h>
#include <binder/Parcel.h>
namespace android {
@@ -31,7 +30,11 @@
int64_t vsyncId = INVALID_VSYNC_ID;
// The id of the input event that caused this buffer
- int32_t inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID;
+ // Default is android::os::IInputConstants::INVALID_INPUT_EVENT_ID = 0
+ // We copy the value of the input event ID instead of including the header, because libgui
+ // header libraries containing FrameTimelineInfo must be available to vendors, but libinput is
+ // not directly vendor available.
+ int32_t inputEventId = 0;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 35990d1..ccd6d4e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -18,6 +18,7 @@
#include <android/gui/DisplayBrightness.h>
#include <android/gui/IFpsListener.h>
+#include <android/gui/IHdrLayerInfoListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <binder/IBinder.h>
@@ -267,6 +268,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;
@@ -424,6 +432,25 @@
const gui::DisplayBrightness& brightness) = 0;
/*
+ * Adds a listener that receives HDR layer information. This is used in combination
+ * with setDisplayBrightness to adjust the display brightness depending on factors such
+ * as whether or not HDR is in use.
+ *
+ * Returns NO_ERROR upon success or NAME_NOT_FOUND if the display is invalid.
+ */
+ virtual status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) = 0;
+ /*
+ * Removes a listener that was added with addHdrLayerInfoListener.
+ *
+ * Returns NO_ERROR upon success, NAME_NOT_FOUND if the display is invalid, and BAD_VALUE if
+ * the listener wasn't registered.
+ *
+ */
+ virtual status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) = 0;
+
+ /*
* Sends a power boost to the composer. This function is asynchronous.
*
* boostId
@@ -459,7 +486,7 @@
* Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) = 0;
+ int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
* Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
@@ -570,6 +597,9 @@
GET_DYNAMIC_DISPLAY_INFO,
ADD_FPS_LISTENER,
REMOVE_FPS_LISTENER,
+ OVERRIDE_HDR_TYPES,
+ ADD_HDR_LAYER_INFO_LISTENER,
+ REMOVE_HDR_LAYER_INFO_LISTENER,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 9274777..41a022f 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -86,7 +86,6 @@
eDeferTransaction_legacy = 0x00000200,
eReleaseBufferListenerChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
- eReparentChildren = 0x00001000,
/* was eDetachChildren, now available 0x00002000, */
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -106,7 +105,7 @@
eHasListenerCallbacksChanged = 0x20000000,
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
- eFrameChanged = 0x1'00000000,
+ /* was eFrameChanged, now available 0x1'00000000, */
eCachedBufferChanged = 0x2'00000000,
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
@@ -127,6 +126,7 @@
void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ bool hasBufferChanges() const;
struct matrix22_t {
float dsdx{0};
@@ -207,7 +207,7 @@
// Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
float frameRate;
int8_t frameRateCompatibility;
- bool shouldBeSeamless;
+ int8_t changeFrameRateStrategy;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -307,10 +307,11 @@
//
// @param frameRate the frame rate in Hz
// @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_*
+// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_*
// @param functionName calling function or nullptr. Used for logging
// @param privileged whether caller has unscoped surfaceflinger access
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName,
- bool privileged = false);
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+ const char* functionName, bool privileged = false);
struct CaptureArgs {
const static int32_t UNSET_UID = -1;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5881221..d22bdaa 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -187,7 +187,8 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
+ virtual status_t setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy);
virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
virtual status_t getExtraBufferCount(int* extraBuffers) const;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index a643e9e..1590b10 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -215,6 +215,11 @@
static status_t setDisplayBrightness(const sp<IBinder>& displayToken,
const gui::DisplayBrightness& brightness);
+ static status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener);
+ static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener);
+
/*
* Sends a power boost to the composer. This function is asynchronous.
*
@@ -456,13 +461,7 @@
Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
const sp<SurfaceControl>& barrierSurfaceControl,
uint64_t frameNumber);
- // Reparents all children of this layer to the new parent handle.
- Transaction& reparentChildren(const sp<SurfaceControl>& sc,
- const sp<SurfaceControl>& newParent);
-
/// Reparents the current layer to the new parent handle. The new parent must not be null.
- // This can be used instead of reparentChildren if the caller wants to
- // only re-parent a specific child.
Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent);
Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
@@ -474,7 +473,6 @@
Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
- Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
ReleaseBufferCallback callback = nullptr);
Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
@@ -515,7 +513,7 @@
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
- int8_t compatibility, bool shouldBeSeamless);
+ int8_t compatibility, int8_t changeFrameRateStrategy);
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -572,6 +570,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);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fe48d88..9b1f0db 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -140,7 +140,6 @@
/*parent*/ nullptr);
t.setLayerStack(mSurfaceControl, 0)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .setFrame(mSurfaceControl, Rect(resolution))
.show(mSurfaceControl)
.setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
.apply();
@@ -218,13 +217,13 @@
col >= region.left - border && col < region.right + border;
}
if (!outsideRegion && inRegion) {
- EXPECT_GE(epsilon, abs(r - *(pixel)));
- EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
- EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ ASSERT_GE(epsilon, abs(r - *(pixel)));
+ ASSERT_GE(epsilon, abs(g - *(pixel + 1)));
+ ASSERT_GE(epsilon, abs(b - *(pixel + 2)));
} else if (outsideRegion && !inRegion) {
- EXPECT_GE(epsilon, abs(r - *(pixel)));
- EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
- EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ ASSERT_GE(epsilon, abs(r - *(pixel)));
+ ASSERT_GE(epsilon, abs(g - *(pixel + 1)));
+ ASSERT_GE(epsilon, abs(b - *(pixel + 2)));
}
ASSERT_EQ(false, ::testing::Test::HasFailure());
}
@@ -466,7 +465,8 @@
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
}
TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
@@ -523,13 +523,15 @@
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ Rect bounds;
+ bounds.left = finalCropSideLength / 2;
+ bounds.top = 0;
+ bounds.right = bounds.left + finalCropSideLength;
+ bounds.bottom = finalCropSideLength;
+
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b, bounds));
ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b,
- {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
- ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(0, 0, 0,
- {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
- /*border*/ 0, /*outsideRegion*/ true));
+ checkScreenCapture(0, 0, 0, bounds, /*border*/ 0, /*outsideRegion*/ true));
}
class TestProducerListener : public BnProducerListener {
@@ -596,7 +598,6 @@
t.setLayerStack(bgSurface, 0)
.show(bgSurface)
.setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
- .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
.apply();
@@ -619,7 +620,8 @@
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
}
class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 59e5c13..49c44a7 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -47,6 +47,8 @@
using android::os::IInputFlinger;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
namespace android::test {
using Transaction = SurfaceComposerClient::Transaction;
@@ -95,15 +97,6 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
- static std::unique_ptr<InputSurface> makeBlastInputSurface(const sp<SurfaceComposerClient> &scc,
- int width, int height) {
- sp<SurfaceControl> surfaceControl =
- scc->createSurface(String8("Test Buffer Surface"), width, height,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState);
- return std::make_unique<InputSurface>(surfaceControl, width, height);
- }
-
static std::unique_ptr<InputSurface> makeContainerInputSurface(
const sp<SurfaceComposerClient> &scc, int width, int height) {
sp<SurfaceControl> surfaceControl =
@@ -180,16 +173,19 @@
EXPECT_EQ(flags, mev->getFlags() & flags);
}
- ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
+ virtual ~InputSurface() {
+ mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
+ }
- void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
- const sp<SurfaceControl>&)> transactionBody) {
+ virtual void doTransaction(
+ std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+ transactionBody) {
SurfaceComposerClient::Transaction t;
transactionBody(t, mSurfaceControl);
t.apply(true);
}
- void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
+ virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
SurfaceComposerClient::Transaction t;
t.show(mSurfaceControl);
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
@@ -259,6 +255,57 @@
InputConsumer* mInputConsumer;
};
+class BlastInputSurface : public InputSurface {
+public:
+ BlastInputSurface(const sp<SurfaceControl> &sc, const sp<SurfaceControl> &parentSc, int width,
+ int height)
+ : InputSurface(sc, width, height) {
+ mParentSurfaceControl = parentSc;
+ }
+
+ ~BlastInputSurface() = default;
+
+ static std::unique_ptr<BlastInputSurface> makeBlastInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> parentSc =
+ scc->createSurface(String8("Test Parent Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Buffer Surface"), width, height,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ parentSc->getHandle());
+ return std::make_unique<BlastInputSurface>(surfaceControl, parentSc, width, height);
+ }
+
+ void doTransaction(
+ std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+ transactionBody) override {
+ SurfaceComposerClient::Transaction t;
+ transactionBody(t, mParentSurfaceControl);
+ t.apply(true);
+ }
+
+ void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override {
+ SurfaceComposerClient::Transaction t;
+ t.show(mParentSurfaceControl);
+ t.setLayer(mParentSurfaceControl, LAYER_BASE);
+ t.setPosition(mParentSurfaceControl, x, y);
+ t.setCrop(mParentSurfaceControl, crop);
+
+ t.show(mSurfaceControl);
+ t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+ t.setCrop(mSurfaceControl, crop);
+ t.setAlpha(mSurfaceControl, 1);
+ t.apply(true);
+ }
+
+private:
+ sp<SurfaceControl> mParentSurfaceControl;
+};
+
class InputSurfacesTest : public ::testing::Test {
public:
InputSurfacesTest() {
@@ -289,15 +336,12 @@
return InputSurface::makeColorInputSurface(mComposerClient, width, height);
}
- void postBuffer(const sp<SurfaceControl> &layer) {
- // wait for previous transactions (such as setSize) to complete
- Transaction().apply(true);
- ANativeWindow_Buffer buffer = {};
- EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
- ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
- // Request an empty transaction to get applied synchronously to ensure the buffer is
- // latched.
- Transaction().apply(true);
+ void postBuffer(const sp<SurfaceControl> &layer, int32_t w, int32_t h) {
+ int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test");
+ Transaction().setBuffer(layer, buffer).apply(true);
usleep(mBufferPostDelay);
}
@@ -474,8 +518,8 @@
// Original bug ref: b/120839715
TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> bufferSurface =
- InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
bufferSurface->showAt(10, 10);
@@ -483,16 +527,16 @@
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
- postBuffer(bufferSurface->mSurfaceControl);
+ postBuffer(bufferSurface->mSurfaceControl, 100, 100);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
}
TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> bufferSurface =
- InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
- postBuffer(bufferSurface->mSurfaceControl);
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
+ postBuffer(bufferSurface->mSurfaceControl, 100, 100);
bgSurface->showAt(10, 10);
bufferSurface->showAt(10, 10);
@@ -720,8 +764,8 @@
TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> bufferSurface =
- InputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
bufferSurface->mInputInfo.ownerUid = 22222;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8d7f8c9..751b95a 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;
}
@@ -815,6 +819,16 @@
return NO_ERROR;
}
+ status_t addHdrLayerInfoListener(const sp<IBinder>&,
+ const sp<gui::IHdrLayerInfoListener>&) override {
+ return NO_ERROR;
+ }
+
+ status_t removeHdrLayerInfoListener(const sp<IBinder>&,
+ const sp<gui::IHdrLayerInfoListener>&) override {
+ return NO_ERROR;
+ }
+
status_t addRegionSamplingListener(const Rect& /*samplingArea*/,
const sp<IBinder>& /*stopLayerHandle*/,
const sp<IRegionSamplingListener>& /*listener*/) override {
@@ -855,7 +869,7 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
- int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override {
+ int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
return NO_ERROR;
}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index ffcc1cd..31027b6 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -231,6 +231,13 @@
mSensors.insert_or_assign(info.type, info);
}
+void InputDeviceInfo::addBatteryInfo(const InputDeviceBatteryInfo& info) {
+ if (mBatteries.find(info.id) != mBatteries.end()) {
+ ALOGW("Battery id %d already exists, will be replaced by new battery added.", info.id);
+ }
+ mBatteries.insert_or_assign(info.id, info);
+}
+
void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) {
if (mLights.find(info.id) != mLights.end()) {
ALOGW("Light id %d already exists, will be replaced by new light added.", info.id);
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index c2a3cf1..f7962e0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -110,6 +110,12 @@
return true;
case Type::DRAG:
return true;
+ case Type::TIMELINE:
+ const nsecs_t gpuCompletedTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ return presentTime > gpuCompletedTime;
}
}
return false;
@@ -129,6 +135,8 @@
return sizeof(Header) + body.capture.size();
case Type::DRAG:
return sizeof(Header) + body.drag.size();
+ case Type::TIMELINE:
+ return sizeof(Header) + body.timeline.size();
}
return sizeof(Header);
}
@@ -260,6 +268,11 @@
msg->body.drag.isExiting = body.drag.isExiting;
break;
}
+ case InputMessage::Type::TIMELINE: {
+ msg->body.timeline.eventId = body.timeline.eventId;
+ msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
+ break;
+ }
}
}
@@ -629,7 +642,7 @@
return mChannel->sendMessage(&msg);
}
-android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
+android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
}
@@ -639,16 +652,24 @@
if (result) {
return android::base::Error(result);
}
- if (msg.header.type != InputMessage::Type::FINISHED) {
- 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);
+ if (msg.header.type == InputMessage::Type::FINISHED) {
+ return Finished{
+ .seq = msg.header.seq,
+ .handled = msg.body.finished.handled,
+ .consumeTime = msg.body.finished.consumeTime,
+ };
}
- return Finished{
- .seq = msg.header.seq,
- .handled = msg.body.finished.handled,
- .consumeTime = msg.body.finished.consumeTime,
- };
+
+ if (msg.header.type == InputMessage::Type::TIMELINE) {
+ return Timeline{
+ .inputEventId = msg.body.timeline.eventId,
+ .graphicsTimeline = msg.body.timeline.graphicsTimeline,
+ };
+ }
+
+ ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
+ mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+ return android::base::Error(UNKNOWN_ERROR);
}
// --- InputConsumer ---
@@ -785,7 +806,8 @@
break;
}
- case InputMessage::Type::FINISHED: {
+ case InputMessage::Type::FINISHED:
+ case InputMessage::Type::TIMELINE: {
LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
"InputConsumer!",
NamedEnum::string(mMsg.header.type).c_str());
@@ -1193,6 +1215,24 @@
return sendUnchainedFinishedSignal(seq, handled);
}
+status_t InputConsumer::sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+ mChannel->getName().c_str(), inputEventId,
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TIMELINE;
+ msg.header.seq = 0;
+ msg.body.timeline.eventId = inputEventId;
+ msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
+ return mChannel->sendMessage(&msg);
+}
+
nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
auto it = mConsumeTimes.find(seq);
// Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
@@ -1399,6 +1439,19 @@
toString(msg.body.drag.isExiting));
break;
}
+ case InputMessage::Type::TIMELINE: {
+ const nsecs_t gpuCompletedTime =
+ msg.body.timeline
+ .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ out += android::base::StringPrintf("inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64
+ ", presentTime=%" PRId64,
+ msg.body.timeline.eventId, gpuCompletedTime,
+ presentTime);
+ break;
+ }
}
out += "\n";
}
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index fc31715..088e00b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -124,13 +124,15 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- 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)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -264,13 +266,15 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- 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)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_FALSE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -304,14 +308,16 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
- ASSERT_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)
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -343,13 +349,15 @@
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)
- << "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)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -385,16 +393,34 @@
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(result->handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(result->consumeTime, publishTime)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
+TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
+ const int32_t inputEventId = 20;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 30;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 40;
+ status_t status = mConsumer->sendTimeline(inputEventId, graphicsTimeline);
+ ASSERT_EQ(OK, status);
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result));
+ const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result);
+ ASSERT_EQ(inputEventId, timeline.inputEventId);
+ ASSERT_EQ(graphicsTimeline, timeline.graphicsTimeline);
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 3d80b38..585779e 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -96,6 +96,10 @@
CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
+
+ CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
+ CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
}
void TestHeaderSize() {
@@ -117,6 +121,9 @@
static_assert(sizeof(InputMessage::Body::Focus) == 8);
static_assert(sizeof(InputMessage::Body::Capture) == 8);
static_assert(sizeof(InputMessage::Body::Drag) == 16);
+ // Timeline
+ static_assert(GraphicsTimeline::SIZE == 2);
+ static_assert(sizeof(InputMessage::Body::Timeline) == 24);
}
// --- VerifiedInputEvent ---
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b406a9c..ada689a 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,8 +159,8 @@
}
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
- return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility,
- /*shouldBeSeamless*/ true);
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
}
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -170,12 +170,12 @@
window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
}
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) {
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
return -EINVAL;
}
- return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless);
+ return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
/**************************************************************************************************
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 285f2fb..61b3f94 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -157,6 +157,7 @@
* For all of these parameters, if 0 is supplied then the window's base
* value will come back in force.
*
+ * \param window pointer to an ANativeWindow object.
* \param width width of the buffers in pixels.
* \param height height of the buffers in pixels.
* \param format one of the AHardwareBuffer_Format constants.
@@ -191,6 +192,7 @@
*
* Available since API level 26.
*
+ * \param window pointer to an ANativeWindow object.
* \param transform combination of {@link ANativeWindowTransform} flags
* \return 0 for success, or -EINVAL if \p transform is invalid
*/
@@ -208,6 +210,7 @@
*
* Available since API level 28.
*
+ * \param window pointer to an ANativeWindow object.
* \param dataSpace data space of all buffers queued after this call.
* \return 0 for success, -EINVAL if window is invalid or the dataspace is not
* supported.
@@ -247,9 +250,10 @@
};
/**
- * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true).
+ * Same as ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
*
- * See ANativeWindow_setFrameRateWithSeamlessness().
+ * See ANativeWindow_setFrameRateWithChangeStrategy().
*
* Available since API level 30.
*/
@@ -267,6 +271,19 @@
*/
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
+/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_ChangeFrameRateStrategy {
+ /**
+ * Change the frame rate only if the transition is going to be seamless.
+ */
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0,
+ /**
+ * Change the frame rate even if the transition is going to be non-seamless,
+ * i.e. with visual interruptions for the user.
+ */
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
+} __INTRODUCED_IN(31);
+
/**
* Sets the intended frame rate for this window.
*
@@ -292,21 +309,22 @@
* valid refresh rate for this device's display - e.g., it's fine to pass 30fps
* to a device that can only run the display at 60fps.
*
+ * \param window pointer to an ANativeWindow object.
+ *
* \param compatibility The frame rate compatibility of this window. The
* compatibility value may influence the system's choice of display refresh
* rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
*
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
*
* \return 0 for success, -EINVAL if the window, frame rate, or compatibility
* value are invalid.
*/
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31);
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy)
+ __INTRODUCED_IN(31);
#ifdef __cplusplus
};
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7aa2cf4..cc82bb4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1019,9 +1019,9 @@
}
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) {
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
- (int)compatibility, (int)shouldBeSeamless);
+ (int)compatibility, (int)changeFrameRateStrategy);
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 24d0e3b..988132c 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -47,7 +47,7 @@
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setFrameRate; # introduced=30
- ANativeWindow_setFrameRateWithSeamlessness; # introduced=31
+ ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 026b19a..ec39137 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -91,6 +91,7 @@
"skia/debug/CaptureTimer.cpp",
"skia/debug/CommonPool.cpp",
"skia/debug/SkiaCapture.cpp",
+ "skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
],
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 06a1722..cd7a86b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -73,6 +73,7 @@
bool cleanupPostRender(CleanupMode mode) override;
int getContextPriority() override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+ void onPrimaryDisplaySizeChanged(ui::Size size) override {}
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
// Creates an output image for rendering to
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 7c51f1b..a69d1f0 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -153,6 +153,10 @@
virtual bool supportsProtectedContent() const = 0;
virtual bool useProtectedContext(bool useProtectedContext) = 0;
+ // Notify RenderEngine of changes to the dimensions of the primary display
+ // so that it can configure its internal caches accordingly.
+ virtual void onPrimaryDisplaySizeChanged(ui::Size size) = 0;
+
// Renders layers for a particular display via GPU composition. This method
// should be called for every display that needs to be rendered via the GPU.
// @param display The display-wide settings that should be applied prior to
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 5f75b81..228553d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -55,6 +55,7 @@
MOCK_METHOD0(cleanFramebufferCache, void());
MOCK_METHOD0(getContextPriority, int());
MOCK_METHOD0(supportsBackgroundBlur, bool());
+ MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size));
};
} // namespace mock
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f8dd7f5..1db20c0 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include "Cache.h"
#include "AutoBackendTexture.h"
#include "SkiaRenderEngine.h"
@@ -28,8 +27,26 @@
namespace android::renderengine::skia {
-static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
- sp<GraphicBuffer> dstBuffer) {
+namespace {
+// Warming shader cache, not framebuffer cache.
+constexpr bool kUseFrameBufferCache = false;
+
+// clang-format off
+// Any non-identity matrix will do.
+const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f,
+ 0.f, 0.7f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 67.3f, 52.2f, 0.f, 1.f);
+// clang-format on
+// When choosing dataspaces below, whether the match the destination or not determined whether
+// a color correction effect is added to the shader. There may be other additional shader details
+// for particular color spaces.
+// TODO(b/184842383) figure out which color related shaders are necessary
+constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+} // namespace
+
+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);
@@ -39,6 +56,7 @@
.boundaries = rect,
.roundedCornersCrop = rect,
},
+ // drawShadow ignores alpha
.shadow =
ShadowSettings{
.ambientColor = vec4(0, 0, 0, 0.00935997f),
@@ -47,24 +65,29 @@
.lightRadius = 2200.0f,
.length = 0.955342f,
},
+ // important that this matches dest so the general shadow fragment shader doesn't
+ // have color correction added, and important that it be srgb, so the *vertex* shader
+ // doesn't have color correction added.
+ .sourceDataspace = kDestDataSpace,
};
auto layers = std::vector<const LayerSettings*>{&layer};
- // The identity matrix will generate the fast 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, false /* useFrameBufferCache*/,
+ // The identity matrix will generate the fast shader
+ renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(),
+ nullptr);
+ // This matrix, which has different scales for x and y, will
+ // generate the slower (more general case) version, which has variants for translucent
+ // casters and rounded rects.
+ // clang-format off
+ layer.geometry.positionTransform = mat4(0.7f, 0.f, 0.f, 0.f,
+ 0.f, 0.8f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);
+ // clang-format on
+ for (auto translucent : {false, true}) {
+ layer.shadow.casterIsTranslucent = translucent;
+ renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
base::unique_fd(), nullptr);
- renderengine->assertShadersCompiled(identity ? 1 : 2);
- identity = false;
}
}
@@ -76,14 +99,6 @@
.geometry =
Geometry{
.boundaries = rect,
- // This matrix is based on actual data seen when opening the dialer.
- // What seems to be important in matching the actual use cases are:
- // - it is not identity
- // - the layer will be drawn (not clipped out etc)
- .positionTransform = mat4(.19f, .0f, .0f, .0f,
- .0f, .19f, .0f, .0f,
- .0f, .0f, 1.f, .0f,
- 169.f, 1527.f, .0f, 1.f),
.roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
@@ -94,28 +109,96 @@
}},
};
+ auto threeCornerRadii = {0.0f, 0.05f, 50.f};
+ auto oneCornerRadius = {50.f};
+
// Test both drawRect and drawRRect
auto layers = std::vector<const LayerSettings*>{&layer};
- for (float roundedCornersRadius : {0.0f, 500.f}) {
- 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 identity : {true, false}) {
+ layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
+ // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
+ // due to animating corner radius.
+ // For the non-idenity matrix, only the large corner radius will create a new shader.
+ for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
+ // roundedCornersCrop is always set, but it is this radius that triggers the behavior
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
for (bool isOpaque : {true, false}) {
layer.source.buffer.isOpaque = isOpaque;
for (auto alpha : {half(.23999f), half(1.0f)}) {
layer.alpha = alpha;
- renderengine->drawLayers(display, layers, dstBuffer,
- false /* useFrameBufferCache*/, base::unique_fd(),
- nullptr);
- renderengine->assertShadersCompiled(1);
+ 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};
+ for (auto transform : {mat4(), kScaleAndTranslate}) {
+ layer.geometry.positionTransform = transform;
+ for (float roundedCornersRadius : {0.0f, 0.05f, 50.f}) {
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+ }
+}
+
+static void drawBlurLayers(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,
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (int radius : {9, 60}) {
+ layer.backgroundBlurRadius = radius;
+ renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
+ base::unique_fd(), nullptr);
+ }
+}
+
+//
+// The collection of shaders cached here were found by using perfetto to record shader compiles
+// during actions that involve RenderEngine, logging the layer settings, and the shader code
+// and reproducing those settings here.
+//
+// It is helpful when debugging this to turn on
+// in SkGLRenderEngine.cpp:
+// kPrintLayerSettings = true
+// kFlushAfterEveryLayer = true
+// in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+// gPrintSKSL = true
+//
+// TODO(b/184631553) cache the shader involved in youtube pip return.
void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+ const int previousCount = renderengine->reportShadersCompiled();
+ if (previousCount) {
+ ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
+ }
const nsecs_t timeBefore = systemTime();
// The dimensions should not matter, so long as we draw inside them.
const Rect displayRect(0, 0, 1080, 2340);
@@ -123,7 +206,7 @@
.physicalDisplay = displayRect,
.clip = displayRect,
.maxLuminance = 500,
- .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ .outputDataspace = kDestDataSpace,
};
const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -133,18 +216,35 @@
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. We only need one version of
- // the shadow's SkSL, so draw it here, giving us both a placeholder image
- // and a chance to compile the shadow's SkSL.
+ // 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");
- drawShadowLayer(renderengine, display, srcBuffer);
+ drawSolidLayers(renderengine, display, dstBuffer);
+ drawShadowLayers(renderengine, display, srcBuffer);
+ drawBlurLayers(renderengine, display, dstBuffer);
+ // The majority of shaders are related to sampling images.
drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+
+ // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
+
+ sp<GraphicBuffer> externalBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usageExternal, "primeShaderCache_external");
+ // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
+ // between 6 and 8 will occur in real uses.
+ drawImageLayers(renderengine, display, dstBuffer, externalBuffer);
+ renderengine->unbindExternalTextureBuffer(externalBuffer->getId());
+
+ renderengine->unbindExternalTextureBuffer(srcBuffer->getId());
+
const nsecs_t timeAfter = systemTime();
const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+ const int shadersCompiled = renderengine->reportShadersCompiled();
+ ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
}
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 7d711c1..3b2c7e5 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -28,6 +28,7 @@
#include <SkColorFilter.h>
#include <SkColorMatrix.h>
#include <SkColorSpace.h>
+#include <SkGraphics.h>
#include <SkImage.h>
#include <SkImageFilters.h>
#include <SkRegion.h>
@@ -40,13 +41,13 @@
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
-#include "Cache.h"
#include <cmath>
#include <cstdint>
#include <memory>
#include "../gl/GLExtensions.h"
+#include "Cache.h"
#include "ColorSpaces.h"
#include "SkBlendMode.h"
#include "SkImageInfo.h"
@@ -54,8 +55,15 @@
#include "filters/LinearEffect.h"
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
+#include "skia/debug/SkiaMemoryReporter.h"
#include "system/graphics-base-v1.0.h"
+namespace {
+// Debugging settings
+static const bool kPrintLayerSettings = false;
+static const bool kFlushAfterEveryLayer = false;
+} // namespace
+
bool checkGlError(const char* op, int lineNumber);
namespace android {
@@ -285,6 +293,10 @@
numShaders, cached);
}
+int SkiaGLRenderEngine::reportShadersCompiled() {
+ return mSkSLCacheMonitor.shadersCachedSinceLastCall();
+}
+
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
@@ -294,12 +306,13 @@
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
mProtectedPlaceholderSurface(protectedPlaceholder),
+ mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)),
mUseColorManagement(args.useColorManagement) {
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
- options.fPreferExternalImagesOverES3 = true;
+ options.fDisableDriverCorrectnessWorkarounds = true;
options.fDisableDistanceFieldPaths = true;
options.fPersistentCache = &mSkSLCacheMonitor;
mGrContext = GrDirectContext::MakeGL(glInterface, options);
@@ -479,18 +492,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) {
@@ -721,6 +747,17 @@
for (const auto& layer : layers) {
ATRACE_NAME("DrawLayer");
+ if (kPrintLayerSettings) {
+ std::stringstream ls;
+ PrintTo(*layer, &ls);
+ auto debugs = ls.str();
+ int pos = 0;
+ while (pos < debugs.size()) {
+ ALOGD("cache_debug %s", debugs.substr(pos, 1000).c_str());
+ pos += 1000;
+ }
+ }
+
sk_sp<SkImage> blurInput;
if (blurCompositionLayer == layer) {
LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
@@ -842,15 +879,15 @@
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 =
@@ -942,6 +979,10 @@
} else {
canvas->drawRect(bounds, paint);
}
+ if (kFlushAfterEveryLayer) {
+ ATRACE_NAME("flush surface");
+ activeSurface->flush();
+ }
}
surfaceAutoSaveRestore.restore();
mCapture->endCapture();
@@ -1174,6 +1215,27 @@
return value;
}
+void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
+ // This cache multiplier was selected based on review of cache sizes relative
+ // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
+ // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
+ // conservative default based on that analysis.
+ const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
+ const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
+
+ // start by resizing the current context
+ auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ grContext->setResourceCacheLimit(maxResourceBytes);
+
+ // if it is possible to switch contexts then we will resize the other context
+ if (useProtectedContext(!mInProtectedContext)) {
+ grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ grContext->setResourceCacheLimit(maxResourceBytes);
+ // reset back to the initial context that was active when this method was called
+ useProtectedContext(!mInProtectedContext);
+ }
+}
+
void SkiaGLRenderEngine::dump(std::string& result) {
const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
@@ -1189,9 +1251,38 @@
StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
mSkSLCacheMonitor.shadersCachedSinceLastCall());
+ std::vector<ResourcePair> cpuResourceMap = {
+ {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
+ {"skia/sk_resource_cache/rrect-blur_", "Masks"},
+ {"skia/sk_resource_cache/rects-blur_", "Masks"},
+ {"skia/sk_resource_cache/tessellated", "Shadows"},
+ {"skia", "Other"},
+ };
+ SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
+ SkGraphics::DumpMemoryStatistics(&cpuReporter);
+ StringAppendF(&result, "Skia CPU Caches: ");
+ cpuReporter.logTotals(result);
+ cpuReporter.logOutput(result);
+
{
std::lock_guard<std::mutex> lock(mRenderingMutex);
- StringAppendF(&result, "RenderEngine texture cache size: %zu\n", mTextureCache.size());
+
+ std::vector<ResourcePair> gpuResourceMap = {
+ {"texture_renderbuffer", "Texture/RenderBuffer"},
+ {"texture", "Texture"},
+ {"gr_text_blob_cache", "Text"},
+ {"skia", "Other"},
+ };
+ SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
+ mGrContext->dumpMemoryStatistics(&gpuReporter);
+ StringAppendF(&result, "Skia's GPU Caches: ");
+ gpuReporter.logTotals(result);
+ gpuReporter.logOutput(result);
+ StringAppendF(&result, "Skia's Wrapped Objects:\n");
+ gpuReporter.logOutput(result, true);
+
+ StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
+ mTextureCache.size());
StringAppendF(&result, "Dumping buffer ids...\n");
// TODO(178539829): It would be nice to know which layer these are coming from and what
// the texture sizes are.
@@ -1199,7 +1290,16 @@
StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
}
StringAppendF(&result, "\n");
- StringAppendF(&result, "RenderEngine protected texture cache size: %zu\n",
+
+ SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
+ mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
+ StringAppendF(&result, "Skia's GPU Protected Caches: ");
+ gpuProtectedReporter.logTotals(result);
+ gpuProtectedReporter.logOutput(result);
+ StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
+ gpuProtectedReporter.logOutput(result, true);
+
+ StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
mProtectedTextureCache.size());
StringAppendF(&result, "Dumping buffer ids...\n");
for (const auto& [id, unused] : mProtectedTextureCache) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 7605df9..8e77c16 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -65,6 +65,8 @@
bool useProtectedContext(bool useProtectedContext) override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
void assertShadersCompiled(int numShaders) override;
+ void onPrimaryDisplaySizeChanged(ui::Size size) override;
+ int reportShadersCompiled() override;
protected:
void dump(std::string& result) override;
@@ -109,6 +111,7 @@
EGLSurface mProtectedPlaceholderSurface;
BlurFilter* mBlurFilter = nullptr;
+ const PixelFormat mDefaultPixelFormat;
const bool mUseColorManagement;
// Cache of GL textures that we'll store per GraphicBuffer ID
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 59d7e2f..51ef088 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -59,6 +59,7 @@
virtual bool cleanupPostRender(CleanupMode) override { return true; };
virtual int getContextPriority() override { return 0; }
virtual void assertShadersCompiled(int numShaders) {}
+ virtual int reportShadersCompiled() { return 0; }
};
} // namespace skia
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
new file mode 100644
index 0000000..f24a4f1
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+
+#include "SkiaMemoryReporter.h"
+
+#include <SkString.h>
+#include <android-base/stringprintf.h>
+#include <log/log_main.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+using base::StringAppendF;
+
+SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize)
+ : mResourceMap(resourceMap),
+ mItemize(itemize),
+ mTotalSize("bytes", 0),
+ mPurgeableSize("bytes", 0) {}
+
+const char* SkiaMemoryReporter::mapName(const char* resourceName) {
+ for (auto& resource : mResourceMap) {
+ if (SkStrContains(resourceName, resource.first)) {
+ return resource.second;
+ }
+ }
+ return nullptr;
+}
+
+void SkiaMemoryReporter::resetCurrentElement() {
+ mCurrentElement.clear();
+ mCurrentValues.clear();
+ mIsCurrentValueWrapped = false;
+}
+
+void SkiaMemoryReporter::processCurrentElement() {
+ // compute the top level element name using the map
+ const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str());
+
+ // if we don't have a resource name then we don't know how to label the
+ // data and should abort.
+ if (resourceName == nullptr) {
+ resetCurrentElement();
+ return;
+ }
+
+ // Only count elements that contain "size"; other values just provide metadata.
+ auto sizeResult = mCurrentValues.find("size");
+ if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) {
+ if (!mIsCurrentValueWrapped) {
+ mTotalSize.value += sizeResult->second.value;
+ mTotalSize.count++;
+ }
+ } else {
+ resetCurrentElement();
+ return;
+ }
+
+ // find the purgeable size if one exists
+ auto purgeableResult = mCurrentValues.find("purgeable_size");
+ if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) {
+ mPurgeableSize.value += purgeableResult->second.value;
+ mPurgeableSize.count++;
+ }
+
+ // do we store this element in the wrapped list or the skia managed list
+ auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults;
+
+ // insert a copy of the element and all of its keys. We must make a copy here instead of
+ // std::move() as we will continue to use these values later in the function and again
+ // when we move on to process the next element.
+ results.insert({mCurrentElement, mCurrentValues});
+
+ // insert the item into its mapped category
+ auto result = results.find(resourceName);
+ if (result != results.end()) {
+ auto& resourceValues = result->second;
+ auto totalResult = resourceValues.find(sizeResult->first);
+ if (totalResult != resourceValues.end()) {
+ ALOGE_IF(sizeResult->second.units != totalResult->second.units,
+ "resource units do not match so the sum of resource type (%s) will be invalid",
+ resourceName);
+ totalResult->second.value += sizeResult->second.value;
+ totalResult->second.count++;
+ } else {
+ ALOGE("an entry (%s) should not exist in the results without a size", resourceName);
+ }
+ } else {
+ // only store the size for the top level resource
+ results.insert({resourceName, {{sizeResult->first, sizeResult->second}}});
+ }
+
+ resetCurrentElement();
+}
+
+void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName,
+ const char* units, uint64_t value) {
+ if (mCurrentElement != dumpName) {
+ processCurrentElement();
+ mCurrentElement = dumpName;
+ }
+ mCurrentValues.insert({valueName, {units, value}});
+}
+
+void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) {
+ if (mCurrentElement != dumpName) {
+ processCurrentElement();
+ mCurrentElement = dumpName;
+ }
+ mIsCurrentValueWrapped = isWrappedObject;
+}
+
+void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) {
+ // process the current element before logging
+ processCurrentElement();
+
+ const auto& resultsMap = wrappedResources ? mWrappedResults : mResults;
+
+ // log each individual element based on the resource map
+ for (const auto& resourceCategory : mResourceMap) {
+ // find the named item and print the totals
+ const auto categoryItem = resultsMap.find(resourceCategory.second);
+ if (categoryItem != resultsMap.end()) {
+ auto result = categoryItem->second.find("size");
+ if (result != categoryItem->second.end()) {
+ TraceValue traceValue = convertUnits(result->second);
+ const char* entry = (traceValue.count > 1) ? "entries" : "entry";
+ StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
+ traceValue.value, traceValue.units, traceValue.count, entry);
+ }
+ if (mItemize) {
+ for (const auto& individualItem : resultsMap) {
+ // if the individual item matches the category then print all its details or
+ // in the case of wrapped resources just print the wrapped size
+ const char* categoryMatch = mapName(individualItem.first.c_str());
+ if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) {
+ auto result = individualItem.second.find("size");
+ TraceValue size = convertUnits(result->second);
+ StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(),
+ size.value, size.units);
+ if (!wrappedResources) {
+ for (const auto& itemValues : individualItem.second) {
+ if (strcmp("size", itemValues.first) == 0) {
+ continue;
+ }
+ TraceValue traceValue = convertUnits(itemValues.second);
+ if (traceValue.value == 0.0f) {
+ StringAppendF(&log, " %s[%s]", itemValues.first,
+ traceValue.units);
+ } else {
+ StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
+ traceValue.value, traceValue.units);
+ }
+ }
+ }
+ StringAppendF(&log, "\n");
+ }
+ }
+ }
+ }
+ }
+}
+
+void SkiaMemoryReporter::logTotals(std::string& log) {
+ // process the current element before logging
+ processCurrentElement();
+
+ TraceValue total = convertUnits(mTotalSize);
+ TraceValue purgeable = convertUnits(mPurgeableSize);
+ StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
+ total.value, total.units, purgeable.value, purgeable.units);
+}
+
+SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
+ TraceValue output(value);
+ if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+ output.value = output.value / 1024.0f;
+ output.units = "KB";
+ }
+ if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+ output.value = output.value / 1024.0f;
+ output.units = "MB";
+ }
+ return output;
+}
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.h b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
new file mode 100644
index 0000000..dbbd65b
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkTraceMemoryDump.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// Mapping of resource substrings (1st element) that if found within a trace "dumpName"
+// should be mapped to the category name (2nd element). All char* used in a resourcePair
+// are expected to have a lifetime longer than the SkiaMemoryReporter in which they are used.
+typedef std::pair<const char*, const char*> ResourcePair;
+
+/*
+ * Utility class for logging the CPU/GPU usage of Skia caches in a format that is specific
+ * to RenderEngine. HWUI has a similar logging class, but the data collected and the way
+ * it is formatted and reported on are intended to be unique to each use case.
+ */
+class SkiaMemoryReporter : public SkTraceMemoryDump {
+public:
+ /**
+ * Creates the reporter class that can be populated by various Skia entry points, like
+ * SkGraphics and GrContext, as well as format and log the results.
+ * @param resourceMap An array of values that maps a Skia dumpName into a user defined category.
+ * The first vector entry that matches the dumpName is used for the mapping.
+ * @param itemize if true when logging the categories the individual elements will be printed
+ * directly after the category details are printed. Otherwise, only the category
+ * totals will be printed.
+ */
+ SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize);
+ ~SkiaMemoryReporter() override {}
+
+ void logOutput(std::string& log, bool wrappedResources = false);
+ void logTotals(std::string& log);
+
+ void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
+ uint64_t value) override;
+
+ void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override {
+ // for convenience we just store this in the same format as numerical values
+ dumpNumericValue(dumpName, valueName, value, 0);
+ }
+ void dumpWrappedState(const char* dumpName, bool isWrappedObject) override;
+
+ LevelOfDetail getRequestedDetails() const override {
+ return SkTraceMemoryDump::kLight_LevelOfDetail;
+ }
+
+ bool shouldDumpWrappedObjects() const override { return true; }
+ void setMemoryBacking(const char*, const char*, const char*) override {}
+ void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
+
+private:
+ struct TraceValue {
+ TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
+ TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}
+
+ const char* units;
+ float value;
+ int count;
+ };
+
+ const char* mapName(const char* resourceName);
+ void processCurrentElement();
+ void resetCurrentElement();
+ TraceValue convertUnits(const TraceValue& value);
+
+ const std::vector<ResourcePair>& mResourceMap;
+ const bool mItemize;
+
+ // variables storing the size of all non-wrapped elements being dumped
+ TraceValue mTotalSize;
+ TraceValue mPurgeableSize;
+
+ // variables storing information on the current node being dumped
+ std::string mCurrentElement;
+ std::unordered_map<const char*, TraceValue> mCurrentValues;
+ bool mIsCurrentValueWrapped = false;
+
+ // variable that stores the final format of the data after the individual elements are processed
+ std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mResults;
+ std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mWrappedResults;
+};
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index ec710d9..6dd4161 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -34,7 +34,7 @@
BlurFilter::BlurFilter() {
SkString blurString(R"(
- in shader input;
+ uniform shader input;
uniform float2 in_blurOffset;
uniform float2 in_maxSizeXY;
@@ -60,8 +60,8 @@
mBlurEffect = std::move(blurEffect);
SkString mixString(R"(
- in shader blurredInput;
- in shader originalInput;
+ uniform shader blurredInput;
+ uniform shader originalInput;
uniform float mixFactor;
half4 main(float2 xy) {
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 84af016..8e8e42e 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -390,7 +390,7 @@
static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
shader.append(R"(
- in shader input;
+ uniform shader input;
half4 main(float2 xy) {
float4 c = float4(sample(input, xy));
)");
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 6a91c7c..783e37f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -332,6 +332,21 @@
return resultFuture.get();
}
+void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, size](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
+ instance.onPrimaryDisplaySizeChanged(size);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
} // namespace threaded
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 7694328..117257a 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -66,6 +66,7 @@
void cleanFramebufferCache() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
+ void onPrimaryDisplaySizeChanged(ui::Size size) override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index bdfe04b..538c1d2 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -97,6 +97,34 @@
return mState != State::INVALID;
}
+status_t FenceTime::wait(int timeout) {
+ // See if we already have a cached value we can return.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return NO_ERROR;
+ }
+
+ // Hold a reference to the fence on the stack in case the class'
+ // reference is removed by another thread. This prevents the
+ // fence from being destroyed until the end of this method, where
+ // we conveniently do not have the lock held.
+ sp<Fence> fence;
+ {
+ // With the lock acquired this time, see if we have the cached
+ // value or if we need to poll the fence.
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mFence.get()) {
+ // Another thread set the signal time just before we added the
+ // reference to mFence.
+ return NO_ERROR;
+ }
+ fence = mFence;
+ }
+
+ // Make the system call without the lock held.
+ return fence->wait(timeout);
+}
+
nsecs_t FenceTime::getSignalTime() {
// See if we already have a cached value we can return.
nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index ecba7f7..ac75f43 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -112,6 +112,13 @@
// Returns a snapshot of the FenceTime in its current state.
Snapshot getSnapshot() const;
+ // wait waits for up to timeout milliseconds for the fence to signal. If
+ // the fence signals then NO_ERROR is returned. If the timeout expires
+ // before the fence signals then -ETIME is returned. A timeout of
+ // TIMEOUT_NEVER may be used to indicate that the call should wait
+ // indefinitely for the fence to signal.
+ status_t wait(int timeout);
+
void signalForTest(nsecs_t signalTime);
private:
diff --git a/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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 397a9d7..4e05847 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -961,7 +961,7 @@
// 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, touchState->dragWindow)) {
+ if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
continue;
}
const InputWindowInfo* windowInfo = windowHandle->getInfo();
@@ -2061,7 +2061,7 @@
goto Failed;
}
- addDragEventLocked(entry, tempTouchState);
+ addDragEventLocked(entry);
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
@@ -2318,39 +2318,64 @@
return injectionResult;
}
-void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
- if (entry.pointerCount != 1 || !state.dragWindow) {
+void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
+ const sp<InputWindowHandle> dropWindow =
+ findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
+ false /*addOutsideTargets*/, false /*addPortalWindows*/,
+ true /*ignoreDragWindow*/);
+ if (dropWindow) {
+ vec2 local = dropWindow->getInfo()->transform.transform(x, y);
+ notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+ } else {
+ notifyDropWindowLocked(nullptr, 0, 0);
+ }
+ mDragState.reset();
+}
+
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
+ if (entry.pointerCount != 1 || !mDragState) {
return;
}
+ if (!mDragState->isStartDrag) {
+ mDragState->isStartDrag = true;
+ mDragState->isStylusButtonDownAtStart =
+ (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+ }
+
int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+ // Handle the special case : stylus button no longer pressed.
+ bool isStylusButtonDown = (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+ if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) {
+ finishDragAndDrop(entry.displayId, x, y);
+ return;
+ }
+
const sp<InputWindowHandle> hoverWindowHandle =
- findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+ findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
false /*addOutsideTargets*/, false /*addPortalWindows*/,
true /*ignoreDragWindow*/);
// enqueue drag exit if needed.
- if (hoverWindowHandle != state.dragHoverWindowHandle &&
- !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
- if (state.dragHoverWindowHandle != nullptr) {
- enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+ if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
+ !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
+ if (mDragState->dragHoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/,
+ entry);
}
- state.dragHoverWindowHandle = hoverWindowHandle;
+ mDragState->dragHoverWindowHandle = hoverWindowHandle;
}
// enqueue drag location if needed.
if (hoverWindowHandle != nullptr) {
enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
}
- } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
- maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
- if (state.dragHoverWindowHandle && maskedAction == AMOTION_EVENT_ACTION_UP) {
- vec2 local = state.dragHoverWindowHandle->getInfo()->transform.transform(x, y);
- notifyDropWindowLocked(state.dragHoverWindowHandle->getToken(), local.x, local.y);
- }
- state.dragWindow = nullptr;
- state.dragHoverWindowHandle = nullptr;
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
+ finishDragAndDrop(entry.displayId, x, y);
+ } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ notifyDropWindowLocked(nullptr, 0, 0);
+ mDragState.reset();
}
}
@@ -3290,15 +3315,21 @@
bool gotOne = false;
status_t status = OK;
for (;;) {
- Result<InputPublisher::Finished> result =
- connection->inputPublisher.receiveFinishedSignal();
+ Result<InputPublisher::ConsumerResponse> result =
+ connection->inputPublisher.receiveConsumerResponse();
if (!result.ok()) {
status = result.error().code();
break;
}
- const InputPublisher::Finished& finished = *result;
- d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
- finished.handled, finished.consumeTime);
+
+ if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+ const InputPublisher::Finished& finish =
+ std::get<InputPublisher::Finished>(*result);
+ d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
+ finish.handled, finish.consumeTime);
+ } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+ // TODO(b/167947340): Report this data to LatencyTracker
+ }
gotOne = true;
}
if (gotOne) {
@@ -4445,13 +4476,12 @@
}
}
- // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+ // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
// could just clear the state here.
- if (state.dragWindow &&
- std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+ if (mDragState &&
+ std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
windowHandles.end()) {
- state.dragWindow = nullptr;
- state.dragHoverWindowHandle = nullptr;
+ mDragState.reset();
}
}
@@ -4690,7 +4720,7 @@
// Store the dragging window.
if (isDragDrop) {
- state.dragWindow = toWindowHandle;
+ mDragState = std::make_unique<DragState>(toWindowHandle);
}
found = true;
@@ -4833,6 +4863,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;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 593ec23..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"
@@ -340,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>>
@@ -499,7 +501,8 @@
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, TouchState& state) REQUIRES(mLock);
+ void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
+ void finishDragAndDrop(int32_t displayId, float x, float y) REQUIRES(mLock);
struct TouchOcclusionInfo {
bool hasBlockingOcclusion;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 4165f49..81b3cf0 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,8 +49,6 @@
windows = other.windows;
portalWindows = other.portalWindows;
gestureMonitors = other.gestureMonitors;
- dragHoverWindowHandle = other.dragHoverWindowHandle;
- dragWindow = other.dragWindow;
}
void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index d7a561c..623c6a8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,11 +41,6 @@
std::vector<TouchedMonitor> gestureMonitors;
- // The last drag hover window which could receive the drag event.
- sp<InputWindowHandle> dragHoverWindowHandle;
- // The window being dragged.
- sp<InputWindowHandle> dragWindow;
-
TouchState();
~TouchState();
void reset();
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 1af70a4..7db32e3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -24,6 +24,7 @@
cc_library_headers {
name: "libinputreader_headers",
export_include_dirs: [
+ "controller",
"include",
"mapper",
"mapper/accumulator",
@@ -35,17 +36,16 @@
srcs: [
"EventHub.cpp",
"InputDevice.cpp",
+ "controller/PeripheralController.cpp",
"mapper/accumulator/CursorButtonAccumulator.cpp",
"mapper/accumulator/CursorScrollAccumulator.cpp",
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
"mapper/accumulator/TouchButtonAccumulator.cpp",
- "mapper/BatteryInputMapper.cpp",
"mapper/CursorInputMapper.cpp",
"mapper/ExternalStylusInputMapper.cpp",
"mapper/InputMapper.cpp",
"mapper/JoystickInputMapper.cpp",
"mapper/KeyboardInputMapper.cpp",
- "mapper/LightInputMapper.cpp",
"mapper/MultiTouchInputMapper.cpp",
"mapper/RotaryEncoderInputMapper.cpp",
"mapper/SensorInputMapper.cpp",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 07011f5..e3e6c12 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -70,6 +70,20 @@
static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+// Mapping for input battery class node IDs lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
+ {{"capacity", InputBatteryClass::CAPACITY},
+ {"capacity_level", InputBatteryClass::CAPACITY_LEVEL},
+ {"status", InputBatteryClass::STATUS}};
+
+// Mapping for input battery class node names lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<InputBatteryClass, std::string> BATTERY_NODES =
+ {{InputBatteryClass::CAPACITY, "capacity"},
+ {InputBatteryClass::CAPACITY_LEVEL, "capacity_level"},
+ {InputBatteryClass::STATUS, "status"}};
+
// must be kept in sync with definitions in kernel /drivers/power/supply/power_supply_sysfs.c
static const std::unordered_map<std::string, int32_t> BATTERY_STATUS =
{{"Unknown", BATTERY_STATUS_UNKNOWN},
@@ -349,7 +363,7 @@
virtualKeyMap(nullptr),
ffEffectPlaying(false),
ffEffectId(-1),
- nextLightId(0),
+ associatedDevice(nullptr),
controllerNumber(0),
enabled(true),
isVirtual(fd < 0) {}
@@ -540,32 +554,36 @@
}
// Check the sysfs path for any input device batteries, returns true if battery found.
-bool EventHub::Device::configureBatteryLocked() {
- if (!sysfsRootPath.has_value()) {
- return false;
+bool EventHub::AssociatedDevice::configureBatteryLocked() {
+ nextBatteryId = 0;
+ // Check if device has any battery.
+ const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
+ for (const auto& nodePath : paths) {
+ RawBatteryInfo info;
+ info.id = ++nextBatteryId;
+ info.path = nodePath;
+ info.name = nodePath.filename();
+
+ // Scan the path for all the files
+ // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+ const auto& files = allFilesInPath(nodePath);
+ for (const auto& file : files) {
+ const auto it = BATTERY_CLASSES.find(file.filename().string());
+ if (it != BATTERY_CLASSES.end()) {
+ info.flags |= it->second;
+ }
+ }
+ batteryInfos.insert_or_assign(info.id, info);
+ ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str());
}
- // Check if device has any batteries.
- std::vector<std::filesystem::path> batteryPaths =
- findSysfsNodes(sysfsRootPath.value(), SysfsClass::POWER_SUPPLY);
- // We only support single battery for an input device, if multiple batteries exist only the
- // first one is supported.
- if (batteryPaths.empty()) {
- // Set path to be empty
- sysfsBatteryPath = std::nullopt;
- return false;
- }
- // If a battery exists
- sysfsBatteryPath = batteryPaths[0];
- return true;
+ return !batteryInfos.empty();
}
// Check the sysfs path for any input device lights, returns true if lights found.
-bool EventHub::Device::configureLightsLocked() {
- if (!sysfsRootPath.has_value()) {
- return false;
- }
+bool EventHub::AssociatedDevice::configureLightsLocked() {
+ nextLightId = 0;
// Check if device has any lights.
- const auto& paths = findSysfsNodes(sysfsRootPath.value(), SysfsClass::LEDS);
+ const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
for (const auto& nodePath : paths) {
RawLightInfo info;
info.id = ++nextLightId;
@@ -599,6 +617,7 @@
}
}
lightInfos.insert_or_assign(info.id, info);
+ ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str());
}
return !lightInfos.empty();
}
@@ -963,42 +982,84 @@
return Errorf("Device not found or device has no key layout.");
}
+// Gets the battery info map from battery ID to RawBatteryInfo of the miscellaneous device
+// associated with the device ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawBatteryInfo>& EventHub::getBatteryInfoLocked(
+ int32_t deviceId) const {
+ static const std::unordered_map<int32_t, RawBatteryInfo> EMPTY_BATTERY_INFO = {};
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->associatedDevice) {
+ return EMPTY_BATTERY_INFO;
+ }
+ return device->associatedDevice->batteryInfos;
+}
+
+const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+ std::vector<int32_t> batteryIds;
+
+ for (const auto [id, info] : getBatteryInfoLocked(deviceId)) {
+ batteryIds.push_back(id);
+ }
+
+ return batteryIds;
+}
+
+std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+ std::scoped_lock _l(mLock);
+
+ const auto infos = getBatteryInfoLocked(deviceId);
+
+ auto it = infos.find(batteryId);
+ if (it != infos.end()) {
+ return it->second;
+ }
+
+ return std::nullopt;
+}
+
+// Gets the light info map from light ID to RawLightInfo of the miscellaneous device associated
+// with the deivice ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked(
+ int32_t deviceId) const {
+ static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {};
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->associatedDevice) {
+ return EMPTY_LIGHT_INFO;
+ }
+ return device->associatedDevice->lightInfos;
+}
+
const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
std::vector<int32_t> lightIds;
- if (device != nullptr) {
- for (const auto [id, info] : device->lightInfos) {
- lightIds.push_back(id);
- }
+ for (const auto [id, info] : getLightInfoLocked(deviceId)) {
+ lightIds.push_back(id);
}
+
return lightIds;
}
std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr) {
- auto it = device->lightInfos.find(lightId);
- if (it != device->lightInfos.end()) {
- return it->second;
- }
+ const auto infos = getLightInfoLocked(deviceId);
+
+ auto it = infos.find(lightId);
+ if (it != infos.end()) {
+ return it->second;
}
+
return std::nullopt;
}
std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) {
- return std::nullopt;
- }
-
- auto it = device->lightInfos.find(lightId);
- if (it == device->lightInfos.end()) {
+ const auto infos = getLightInfoLocked(deviceId);
+ auto it = infos.find(lightId);
+ if (it == infos.end()) {
return std::nullopt;
}
std::string buffer;
@@ -1013,13 +1074,9 @@
int32_t deviceId, int32_t lightId) {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) {
- return std::nullopt;
- }
-
- auto lightIt = device->lightInfos.find(lightId);
- if (lightIt == device->lightInfos.end()) {
+ const auto infos = getLightInfoLocked(deviceId);
+ auto lightIt = infos.find(lightId);
+ if (lightIt == infos.end()) {
return std::nullopt;
}
@@ -1056,14 +1113,10 @@
void EventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) {
- ALOGE("Device Id %d does not exist", deviceId);
- return;
- }
- auto lightIt = device->lightInfos.find(lightId);
- if (lightIt == device->lightInfos.end()) {
- ALOGE("Light Id %d does not exist.", lightId);
+ const auto infos = getLightInfoLocked(deviceId);
+ auto lightIt = infos.find(lightId);
+ if (lightIt == infos.end()) {
+ ALOGE("%s lightId %d not found ", __func__, lightId);
return;
}
@@ -1078,13 +1131,9 @@
std::unordered_map<LightColor, int32_t> intensities) {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) {
- ALOGE("Device Id %d does not exist", deviceId);
- return;
- }
- auto lightIt = device->lightInfos.find(lightId);
- if (lightIt == device->lightInfos.end()) {
+ const auto infos = getLightInfoLocked(deviceId);
+ auto lightIt = infos.find(lightId);
+ if (lightIt == infos.end()) {
ALOGE("Light Id %d does not exist.", lightId);
return;
}
@@ -1352,51 +1401,56 @@
return nullptr;
}
-std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId) const {
+std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- std::string buffer;
- if (device == nullptr || !device->sysfsBatteryPath.has_value()) {
+ const auto infos = getBatteryInfoLocked(deviceId);
+ auto it = infos.find(batteryId);
+ if (it == infos.end()) {
return std::nullopt;
}
+ std::string buffer;
// Some devices report battery capacity as an integer through the "capacity" file
- if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
+ if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY),
+ &buffer)) {
return std::stoi(base::Trim(buffer));
}
// Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
// These values are taken from kernel source code include/linux/power_supply.h
- if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
+ if (base::ReadFileToString(it->second.path /
+ BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL),
+ &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;
+ const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer));
+ if (levelIt != BATTERY_LEVEL.end()) {
+ return levelIt->second;
}
}
+
return std::nullopt;
}
-std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId) const {
+std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const {
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- std::string buffer;
-
- if (device == nullptr || !device->sysfsBatteryPath.has_value()) {
+ const auto infos = getBatteryInfoLocked(deviceId);
+ auto it = infos.find(batteryId);
+ if (it == infos.end()) {
return std::nullopt;
}
+ std::string buffer;
- if (!base::ReadFileToString(device->sysfsBatteryPath.value() / "status", &buffer)) {
+ if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS),
+ &buffer)) {
ALOGE("Failed to read sysfs battery info: %s", strerror(errno));
return std::nullopt;
}
// Remove white space like trailing new line
- const auto it = BATTERY_STATUS.find(base::Trim(buffer));
-
- if (it != BATTERY_STATUS.end()) {
- return it->second;
+ const auto statusIt = BATTERY_STATUS.find(base::Trim(buffer));
+ if (statusIt != BATTERY_STATUS.end()) {
+ return statusIt->second;
}
return std::nullopt;
@@ -1879,11 +1933,26 @@
// Load the configuration file for the device.
device->loadConfigurationLocked();
- // Grab the device's sysfs path
- device->sysfsRootPath = getSysfsRootPath(devicePath.c_str());
- // find related components
- bool hasBattery = device->configureBatteryLocked();
- bool hasLights = device->configureLightsLocked();
+ bool hasBattery = false;
+ bool hasLights = false;
+ // Check the sysfs root path
+ std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str());
+ if (sysfsRootPath.has_value()) {
+ std::shared_ptr<AssociatedDevice> associatedDevice;
+ for (const auto& [id, dev] : mDevices) {
+ if (device->identifier.descriptor == dev->identifier.descriptor &&
+ !dev->associatedDevice) {
+ associatedDevice = dev->associatedDevice;
+ }
+ }
+ if (!associatedDevice) {
+ associatedDevice = std::make_shared<AssociatedDevice>(sysfsRootPath.value());
+ }
+ hasBattery = associatedDevice->configureBatteryLocked();
+ hasLights = associatedDevice->configureLightsLocked();
+
+ device->associatedDevice = associatedDevice;
+ }
// Figure out the kinds of events the device reports.
device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index dd1abeb..8f75d22 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -21,14 +21,13 @@
#include <input/Flags.h>
#include <algorithm>
-#include "BatteryInputMapper.h"
#include "CursorInputMapper.h"
#include "ExternalStylusInputMapper.h"
#include "InputReaderContext.h"
#include "JoystickInputMapper.h"
#include "KeyboardInputMapper.h"
-#include "LightInputMapper.h"
#include "MultiTouchInputMapper.h"
+#include "PeripheralController.h"
#include "RotaryEncoderInputMapper.h"
#include "SensorInputMapper.h"
#include "SingleTouchInputMapper.h"
@@ -131,6 +130,9 @@
}
for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
+ if (mController) {
+ mController->dump(dump);
+ }
}
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
@@ -162,22 +164,10 @@
mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
}
- // Battery-like devices. Only one battery mapper for each EventHub device.
- if (classes.test(InputDeviceClass::BATTERY)) {
- InputDeviceInfo deviceInfo;
- getDeviceInfo(&deviceInfo);
- if (!deviceInfo.hasBattery()) {
- mappers.push_back(std::make_unique<BatteryInputMapper>(*contextPtr));
- }
- }
-
- // Light-containing devices. Only one light mapper for each EventHub device.
- if (classes.test(InputDeviceClass::LIGHT)) {
- InputDeviceInfo deviceInfo;
- getDeviceInfo(&deviceInfo);
- if (deviceInfo.getLightIds().empty()) {
- mappers.push_back(std::make_unique<LightInputMapper>(*contextPtr));
- }
+ // Battery-like devices or light-containing devices.
+ // PeripheralController will be created with associated EventHub device.
+ if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
+ mController = std::make_unique<PeripheralController>(*contextPtr);
}
// Keyboard-like devices.
@@ -409,6 +399,10 @@
mHasMic);
for_each_mapper(
[outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
+
+ if (mController) {
+ mController->populateDeviceInfo(outDeviceInfo);
+ }
}
int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -511,38 +505,27 @@
}
std::optional<int32_t> InputDevice::getBatteryCapacity() {
- return first_in_mappers<int32_t>(
- [](InputMapper& mapper) { return mapper.getBatteryCapacity(); });
+ return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt;
}
std::optional<int32_t> InputDevice::getBatteryStatus() {
- return first_in_mappers<int32_t>([](InputMapper& mapper) { return mapper.getBatteryStatus(); });
+ return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt;
}
bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
- bool success = true;
- for_each_mapper([&success, lightId, color](InputMapper& mapper) {
- success &= mapper.setLightColor(lightId, color);
- });
- return success;
+ return mController ? mController->setLightColor(lightId, color) : false;
}
bool InputDevice::setLightPlayerId(int32_t lightId, int32_t playerId) {
- bool success = true;
- for_each_mapper([&success, lightId, playerId](InputMapper& mapper) {
- success &= mapper.setLightPlayerId(lightId, playerId);
- });
- return success;
+ return mController ? mController->setLightPlayerId(lightId, playerId) : false;
}
std::optional<int32_t> InputDevice::getLightColor(int32_t lightId) {
- return first_in_mappers<int32_t>(
- [lightId](InputMapper& mapper) { return mapper.getLightColor(lightId); });
+ return mController ? mController->getLightColor(lightId) : std::nullopt;
}
std::optional<int32_t> InputDevice::getLightPlayerId(int32_t lightId) {
- return first_in_mappers<int32_t>(
- [lightId](InputMapper& mapper) { return mapper.getLightPlayerId(lightId); });
+ return mController ? mController->getLightPlayerId(lightId) : std::nullopt;
}
int32_t InputDevice::getMetaState() {
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
new file mode 100644
index 0000000..1a40d06
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <locale>
+#include <regex>
+
+#include "../Macros.h"
+
+#include "PeripheralController.h"
+#include "input/NamedEnum.h"
+
+// Log detailed debug messages about input device lights.
+static constexpr bool DEBUG_LIGHT_DETAILS = false;
+
+namespace android {
+
+static inline int32_t getAlpha(int32_t color) {
+ return (color >> 24) & 0xff;
+}
+
+static inline int32_t getRed(int32_t color) {
+ return (color >> 16) & 0xff;
+}
+
+static inline int32_t getGreen(int32_t color) {
+ return (color >> 8) & 0xff;
+}
+
+static inline int32_t getBlue(int32_t color) {
+ return color & 0xff;
+}
+
+static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
+ return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
+}
+
+/**
+ * Input controller owned by InputReader device, implements the native API for querying input
+ * lights, getting and setting the lights brightness and color, by interacting with EventHub
+ * devices.
+ */
+PeripheralController::PeripheralController(InputDeviceContext& deviceContext)
+ : mDeviceContext(deviceContext) {
+ configureBattries();
+ configureLights();
+}
+
+PeripheralController::~PeripheralController() {}
+
+std::optional<std::int32_t> PeripheralController::Light::getRawLightBrightness(int32_t rawLightId) {
+ std::optional<RawLightInfo> rawInfoOpt = context.getRawLightInfo(rawLightId);
+ if (!rawInfoOpt.has_value()) {
+ return std::nullopt;
+ }
+ std::optional<int32_t> brightnessOpt = context.getLightBrightness(rawLightId);
+ if (!brightnessOpt.has_value()) {
+ return std::nullopt;
+ }
+ int brightness = brightnessOpt.value();
+
+ // If the light node doesn't have max brightness, use the default max brightness.
+ int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
+ float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+ // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
+ if (rawMaxBrightness != MAX_BRIGHTNESS) {
+ brightness = brightness * ratio;
+ }
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+ brightness, ratio);
+ }
+ return brightness;
+}
+
+void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
+ std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
+ if (!rawInfo.has_value()) {
+ return;
+ }
+ // If the light node doesn't have max brightness, use the default max brightness.
+ int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
+ float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+ // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
+ if (rawMaxBrightness != MAX_BRIGHTNESS) {
+ brightness = ceil(brightness / ratio);
+ }
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+ brightness, ratio);
+ }
+ context.setLightBrightness(rawLightId, brightness);
+}
+
+bool PeripheralController::SingleLight::setLightColor(int32_t color) {
+ int32_t brightness = getAlpha(color);
+ setRawLightBrightness(rawId, brightness);
+
+ return true;
+}
+
+bool PeripheralController::RgbLight::setLightColor(int32_t color) {
+ // Compose color value as per:
+ // https://developer.android.com/reference/android/graphics/Color?hl=en
+ // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+ // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
+ // MAX_BRIGHTNESS.
+ brightness = getAlpha(color);
+ int32_t red = 0;
+ int32_t green = 0;
+ int32_t blue = 0;
+ if (brightness > 0) {
+ float ratio = MAX_BRIGHTNESS / brightness;
+ red = ceil(getRed(color) / ratio);
+ green = ceil(getGreen(color) / ratio);
+ blue = ceil(getBlue(color) / ratio);
+ }
+ setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
+ setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
+ setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
+ if (rawGlobalId.has_value()) {
+ setRawLightBrightness(rawGlobalId.value(), brightness);
+ }
+
+ return true;
+}
+
+bool PeripheralController::MultiColorLight::setLightColor(int32_t color) {
+ std::unordered_map<LightColor, int32_t> intensities;
+ intensities.emplace(LightColor::RED, getRed(color));
+ intensities.emplace(LightColor::GREEN, getGreen(color));
+ intensities.emplace(LightColor::BLUE, getBlue(color));
+
+ context.setLightIntensities(rawId, intensities);
+ setRawLightBrightness(rawId, getAlpha(color));
+ return true;
+}
+
+std::optional<int32_t> PeripheralController::SingleLight::getLightColor() {
+ std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+ if (!brightness.has_value()) {
+ return std::nullopt;
+ }
+
+ return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
+}
+
+std::optional<int32_t> PeripheralController::RgbLight::getLightColor() {
+ // If the Alpha component is zero, then return color 0.
+ if (brightness == 0) {
+ return 0;
+ }
+ // Compose color value as per:
+ // https://developer.android.com/reference/android/graphics/Color?hl=en
+ // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+ std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
+ std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
+ std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
+ // If we can't get brightness for any of the RGB light
+ if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
+ return std::nullopt;
+ }
+
+ // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
+ // value, scale it back to return the nominal color value.
+ float ratio = MAX_BRIGHTNESS / brightness;
+ int32_t red = round(redOr.value() * ratio);
+ int32_t green = round(greenOr.value() * ratio);
+ int32_t blue = round(blueOr.value() * ratio);
+
+ if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
+ // Previously stored brightness isn't valid for current LED values, so just reset to max
+ // brightness since an app couldn't have provided these values in the first place.
+ red = redOr.value();
+ green = greenOr.value();
+ blue = blueOr.value();
+ brightness = MAX_BRIGHTNESS;
+ }
+
+ return toArgb(brightness, red, green, blue);
+}
+
+std::optional<int32_t> PeripheralController::MultiColorLight::getLightColor() {
+ auto ret = context.getLightIntensities(rawId);
+ if (!ret.has_value()) {
+ return std::nullopt;
+ }
+ std::unordered_map<LightColor, int32_t> intensities = ret.value();
+ // Get red, green, blue colors
+ int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
+ intensities.at(LightColor::GREEN) /* green */,
+ intensities.at(LightColor::BLUE) /* blue */);
+ // Get brightness
+ std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+ if (brightness.has_value()) {
+ return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
+ }
+ return std::nullopt;
+}
+
+bool PeripheralController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
+ if (rawLightIds.find(playerId) == rawLightIds.end()) {
+ return false;
+ }
+ for (const auto& [id, rawId] : rawLightIds) {
+ if (playerId == id) {
+ setRawLightBrightness(rawId, MAX_BRIGHTNESS);
+ } else {
+ setRawLightBrightness(rawId, 0);
+ }
+ }
+ return true;
+}
+
+std::optional<int32_t> PeripheralController::PlayerIdLight::getLightPlayerId() {
+ for (const auto& [id, rawId] : rawLightIds) {
+ std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+ if (brightness.has_value() && brightness.value() > 0) {
+ return id;
+ }
+ }
+ return std::nullopt;
+}
+
+void PeripheralController::SingleLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void PeripheralController::PlayerIdLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
+ dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
+ for (const auto& [id, rawId] : rawLightIds) {
+ dump += StringPrintf("id %d -> %d ", id, rawId);
+ }
+ dump += "\n";
+}
+
+void PeripheralController::RgbLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+ dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
+ rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+ if (rawGlobalId.has_value()) {
+ dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
+ }
+ dump += "\n";
+}
+
+void PeripheralController::MultiColorLight::dump(std::string& dump) {
+ dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void PeripheralController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+ // TODO: b/180733860 Remove this after enabling multi-battery
+ if (!mBatteries.empty()) {
+ deviceInfo->setHasBattery(true);
+ }
+
+ for (const auto& [batteryId, battery] : mBatteries) {
+ InputDeviceBatteryInfo batteryInfo(battery->name, battery->id);
+ deviceInfo->addBatteryInfo(batteryInfo);
+ }
+
+ for (const auto& [lightId, light] : mLights) {
+ // Input device light doesn't support ordinal, always pass 1.
+ InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+ deviceInfo->addLightInfo(lightInfo);
+ }
+}
+
+void PeripheralController::dump(std::string& dump) {
+ dump += INDENT2 "Input Controller:\n";
+ if (!mLights.empty()) {
+ dump += INDENT3 "Lights:\n";
+ for (const auto& [lightId, light] : mLights) {
+ dump += StringPrintf(INDENT4 "Id: %d", lightId);
+ dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
+ dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+ light->dump(dump);
+ }
+ }
+ // Dump raw lights
+ dump += INDENT3 "RawLights:\n";
+ dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
+ const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+ // Map from raw light id to raw light info
+ std::unordered_map<int32_t, RawLightInfo> rawInfos;
+ for (const auto& rawId : rawLightIds) {
+ std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+ if (!rawInfo.has_value()) {
+ continue;
+ }
+ dump += StringPrintf(INDENT4 "%d", rawId);
+ dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
+ dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
+ dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
+ dump += StringPrintf(INDENT4 "%d\n",
+ getDeviceContext().getLightBrightness(rawId).value_or(-1));
+ }
+
+ if (!mBatteries.empty()) {
+ dump += INDENT3 "Batteries:\n";
+ for (const auto& [batteryId, battery] : mBatteries) {
+ dump += StringPrintf(INDENT4 "Id: %d", batteryId);
+ dump += StringPrintf(INDENT4 "Name: %s", battery->name.c_str());
+ dump += getBatteryCapacity(batteryId).has_value()
+ ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity(batteryId).value())
+ : StringPrintf(INDENT3 "Capacity: Unknown");
+
+ std::string status;
+ switch (getBatteryStatus(batteryId).value_or(BATTERY_STATUS_UNKNOWN)) {
+ case BATTERY_STATUS_CHARGING:
+ status = "Charging";
+ break;
+ case BATTERY_STATUS_DISCHARGING:
+ status = "Discharging";
+ break;
+ case BATTERY_STATUS_NOT_CHARGING:
+ status = "Not charging";
+ break;
+ case BATTERY_STATUS_FULL:
+ status = "Full";
+ break;
+ default:
+ status = "Unknown";
+ }
+ dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
+ }
+ }
+}
+
+void PeripheralController::configureBattries() {
+ // Check raw batteries
+ const std::vector<int32_t> rawBatteryIds = getDeviceContext().getRawBatteryIds();
+
+ for (const auto& rawId : rawBatteryIds) {
+ std::optional<RawBatteryInfo> rawInfo = getDeviceContext().getRawBatteryInfo(rawId);
+ if (!rawInfo.has_value()) {
+ continue;
+ }
+ std::unique_ptr<Battery> battery =
+ std::make_unique<Battery>(getDeviceContext(), rawInfo->name, rawInfo->id);
+ mBatteries.insert_or_assign(rawId, std::move(battery));
+ }
+}
+
+void PeripheralController::configureLights() {
+ bool hasRedLed = false;
+ bool hasGreenLed = false;
+ bool hasBlueLed = false;
+ std::optional<int32_t> rawGlobalId = std::nullopt;
+ // Player ID light common name string
+ std::string playerIdName;
+ // Raw RGB color to raw light ID
+ std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
+ // Map from player Id to raw light Id
+ std::unordered_map<int32_t, int32_t> playerIdLightIds;
+
+ // Check raw lights
+ const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+ // Map from raw light id to raw light info
+ std::unordered_map<int32_t, RawLightInfo> rawInfos;
+ for (const auto& rawId : rawLightIds) {
+ std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+ if (!rawInfo.has_value()) {
+ continue;
+ }
+ rawInfos.insert_or_assign(rawId, rawInfo.value());
+ // Check if this is a group LEDs for player ID
+ std::regex lightPattern("([a-z]+)([0-9]+)");
+ std::smatch results;
+ if (std::regex_match(rawInfo->name, results, lightPattern)) {
+ std::string commonName = results[1].str();
+ int32_t playerId = std::stoi(results[2]);
+ if (playerIdLightIds.empty()) {
+ playerIdName = commonName;
+ playerIdLightIds.insert_or_assign(playerId, rawId);
+ } else {
+ // Make sure the player ID leds have common string name
+ if (playerIdName.compare(commonName) == 0 &&
+ playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
+ playerIdLightIds.insert_or_assign(playerId, rawId);
+ }
+ }
+ }
+ // Check if this is an LED of RGB light
+ if (rawInfo->flags.test(InputLightClass::RED)) {
+ hasRedLed = true;
+ rawRgbIds.emplace(LightColor::RED, rawId);
+ }
+ if (rawInfo->flags.test(InputLightClass::GREEN)) {
+ hasGreenLed = true;
+ rawRgbIds.emplace(LightColor::GREEN, rawId);
+ }
+ if (rawInfo->flags.test(InputLightClass::BLUE)) {
+ hasBlueLed = true;
+ rawRgbIds.emplace(LightColor::BLUE, rawId);
+ }
+ if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
+ rawGlobalId = rawId;
+ }
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
+ rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
+ }
+ }
+
+ // Construct a player ID light
+ if (playerIdLightIds.size() > 1) {
+ std::unique_ptr<Light> light =
+ std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
+ playerIdLightIds);
+ mLights.insert_or_assign(light->id, std::move(light));
+ // Remove these raw lights from raw light info as they've been used to compose a
+ // Player ID light, so we do not expose these raw lights as single lights.
+ for (const auto& [playerId, rawId] : playerIdLightIds) {
+ rawInfos.erase(rawId);
+ }
+ }
+ // Construct a RGB light for composed RGB light
+ if (hasRedLed && hasGreenLed && hasBlueLed) {
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
+ rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+ }
+ std::unique_ptr<Light> light =
+ std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+ mLights.insert_or_assign(light->id, std::move(light));
+ // Remove from raw light info as they've been composed a RBG light.
+ rawInfos.erase(rawRgbIds.at(LightColor::RED));
+ rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
+ rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
+ if (rawGlobalId.has_value()) {
+ rawInfos.erase(rawGlobalId.value());
+ }
+ }
+
+ // Check the rest of raw light infos
+ for (const auto& [rawId, rawInfo] : rawInfos) {
+ // If the node is multi-color led, construct a MULTI_COLOR light
+ if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
+ rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+ }
+ std::unique_ptr<Light> light =
+ std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+ rawInfo.id);
+ mLights.insert_or_assign(light->id, std::move(light));
+ continue;
+ }
+ // Construct a single LED light
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+ }
+ std::unique_ptr<Light> light =
+ std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+ rawInfo.id);
+
+ mLights.insert_or_assign(light->id, std::move(light));
+ }
+}
+
+std::optional<int32_t> PeripheralController::getBatteryCapacity(int batteryId) {
+ return getDeviceContext().getBatteryCapacity(batteryId);
+}
+
+std::optional<int32_t> PeripheralController::getBatteryStatus(int batteryId) {
+ return getDeviceContext().getBatteryStatus(batteryId);
+}
+
+bool PeripheralController::setLightColor(int32_t lightId, int32_t color) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return false;
+ }
+ auto& light = it->second;
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
+ NamedEnum::string(light->type).c_str(), color);
+ }
+ return light->setLightColor(color);
+}
+
+std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return std::nullopt;
+ }
+ auto& light = it->second;
+ std::optional<int32_t> color = light->getLightColor();
+ if (DEBUG_LIGHT_DETAILS) {
+ ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
+ NamedEnum::string(light->type).c_str(), color.value_or(0));
+ }
+ return color;
+}
+
+bool PeripheralController::setLightPlayerId(int32_t lightId, int32_t playerId) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return false;
+ }
+ auto& light = it->second;
+ return light->setLightPlayerId(playerId);
+}
+
+std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) {
+ auto it = mLights.find(lightId);
+ if (it == mLights.end()) {
+ return std::nullopt;
+ }
+ auto& light = it->second;
+ return light->getLightPlayerId();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/controller/PeripheralController.h
similarity index 80%
rename from services/inputflinger/reader/mapper/LightInputMapper.h
rename to services/inputflinger/reader/controller/PeripheralController.h
index 43141b8..ff3607f 100644
--- a/services/inputflinger/reader/mapper/LightInputMapper.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -14,35 +14,49 @@
* limitations under the License.
*/
-#ifndef _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
-#define _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
+#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
+#define _UI_INPUTREADER_LIGHT_CONTROLLER_H
-#include "InputMapper.h"
+#include "PeripheralControllerInterface.h"
namespace android {
-class LightInputMapper : public InputMapper {
+class PeripheralController : public PeripheralControllerInterface {
// Refer to https://developer.android.com/reference/kotlin/android/graphics/Color
/* Number of colors : {red, green, blue} */
static constexpr size_t COLOR_NUM = 3;
static constexpr int32_t MAX_BRIGHTNESS = 0xff;
public:
- explicit LightInputMapper(InputDeviceContext& deviceContext);
- ~LightInputMapper() override;
+ explicit PeripheralController(InputDeviceContext& deviceContext);
+ ~PeripheralController() override;
- uint32_t getSources() override;
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
void dump(std::string& dump) override;
- void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
- void reset(nsecs_t when) override;
- void process(const RawEvent* rawEvent) override;
bool setLightColor(int32_t lightId, int32_t color) override;
bool setLightPlayerId(int32_t lightId, int32_t playerId) override;
std::optional<int32_t> getLightColor(int32_t lightId) override;
std::optional<int32_t> getLightPlayerId(int32_t lightId) override;
+ std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override;
+ std::optional<int32_t> getBatteryStatus(int32_t batteryId) override;
private:
+ inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+ inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+
+ InputDeviceContext& mDeviceContext;
+ void configureLights();
+ void configureBattries();
+
+ struct Battery {
+ explicit Battery(InputDeviceContext& context, const std::string& name, int32_t id)
+ : context(context), name(name), id(id) {}
+ virtual ~Battery() {}
+ InputDeviceContext& context;
+ std::string name;
+ int32_t id;
+ };
+
struct Light {
explicit Light(InputDeviceContext& context, const std::string& name, int32_t id,
InputDeviceLightType type)
@@ -128,8 +142,11 @@
// Light map from light ID to Light
std::unordered_map<int32_t, std::unique_ptr<Light>> mLights;
+
+ // Battery map from battery ID to battery
+ std::unordered_map<int32_t, std::unique_ptr<Battery>> mBatteries;
};
} // namespace android
-#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
+#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H
diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
new file mode 100644
index 0000000..7688a43
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUTREADER_INPUT_CONTROLLER_H
+#define _UI_INPUTREADER_INPUT_CONTROLLER_H
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+/* A peripheral controller manages the input device peripherals associated with the input device,
+ * like the sysfs based battery and light class devices.
+ *
+ */
+class PeripheralControllerInterface {
+public:
+ PeripheralControllerInterface() {}
+ virtual ~PeripheralControllerInterface() {}
+
+ // Interface methods for Battery
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
+ virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0;
+
+ // Interface methods for Light
+ virtual bool setLightColor(int32_t lightId, int32_t color) = 0;
+ virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) = 0;
+ virtual std::optional<int32_t> getLightColor(int32_t lightId) = 0;
+ virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) = 0;
+
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) = 0;
+ virtual void dump(std::string& dump) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 2afaa85..410a706 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -173,6 +173,15 @@
MAX_BRIGHTNESS = 0x00000080,
};
+enum class InputBatteryClass : uint32_t {
+ /* The input device battery has capacity node. */
+ CAPACITY = 0x00000001,
+ /* The input device battery has capacity_level node. */
+ CAPACITY_LEVEL = 0x00000002,
+ /* The input device battery has status node. */
+ STATUS = 0x00000004,
+};
+
/* Describes a raw light. */
struct RawLightInfo {
int32_t id;
@@ -183,6 +192,14 @@
std::filesystem::path path;
};
+/* Describes a raw battery. */
+struct RawBatteryInfo {
+ int32_t id;
+ std::string name;
+ Flags<InputBatteryClass> flags;
+ std::filesystem::path path;
+};
+
/*
* Gets the class that owns an axis, in cases where multiple classes might claim
* the same axis for different purposes.
@@ -263,6 +280,12 @@
virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
int32_t absCode) = 0;
+ // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
+ // containing the raw info of the sysfs node structure.
+ virtual const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) = 0;
+ virtual std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+ int32_t BatteryId) = 0;
+
// Raw lights are sysfs led light nodes we found from the EventHub device sysfs node,
// containing the raw info of the sysfs node structure.
virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0;
@@ -307,10 +330,11 @@
virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
/* Query battery level. */
- virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const = 0;
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+ int32_t batteryId) const = 0;
/* Query battery status. */
- virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) const = 0;
+ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const = 0;
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
virtual void requestReopenDevices() = 0;
@@ -435,6 +459,10 @@
base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
int32_t deviceId, int32_t absCode) override final;
+ const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override final;
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+ int32_t BatteryId) override final;
+
const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final;
std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final;
@@ -485,9 +513,11 @@
void monitor() override final;
- std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const override final;
+ std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+ int32_t batteryId) const override final;
- std::optional<int32_t> getBatteryStatus(int32_t deviceId) const override final;
+ std::optional<int32_t> getBatteryStatus(int32_t deviceId,
+ int32_t batteryId) const override final;
bool isDeviceEnabled(int32_t deviceId) override final;
@@ -498,6 +528,22 @@
~EventHub() override;
private:
+ struct AssociatedDevice {
+ // The device descriptor from evdev device the misc device associated with.
+ std::string descriptor;
+ // The sysfs root path of the misc device.
+ std::filesystem::path sysfsRootPath;
+
+ int32_t nextBatteryId;
+ int32_t nextLightId;
+ std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
+ std::unordered_map<int32_t, RawLightInfo> lightInfos;
+ explicit AssociatedDevice(std::filesystem::path sysfsRootPath)
+ : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {}
+ bool configureBatteryLocked();
+ bool configureLightsLocked();
+ };
+
struct Device {
int fd; // may be -1 if device is closed
const int32_t id;
@@ -527,12 +573,9 @@
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
- // The paths are invalid when they're std::nullopt
- std::optional<std::filesystem::path> sysfsRootPath;
- std::optional<std::filesystem::path> sysfsBatteryPath;
- // maps from light id to light info
- std::unordered_map<int32_t, RawLightInfo> lightInfos;
- int32_t nextLightId;
+ // A shared_ptr of a device associated with the input device.
+ // The input devices with same descriptor has the same associated device.
+ std::shared_ptr<AssociatedDevice> associatedDevice;
int32_t controllerNumber;
@@ -563,8 +606,6 @@
void setLedForControllerLocked();
status_t mapLed(int32_t led, int32_t* outScanCode) const;
void setLedStateLocked(int32_t led, bool on);
- bool configureBatteryLocked();
- bool configureLightsLocked();
};
/**
@@ -616,6 +657,12 @@
void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
Flags<InputDeviceClass> classes) REQUIRES(mLock);
+ const std::unordered_map<int32_t, RawBatteryInfo>& getBatteryInfoLocked(int32_t deviceId) const
+ REQUIRES(mLock);
+
+ const std::unordered_map<int32_t, RawLightInfo>& getLightInfoLocked(int32_t deviceId) const
+ REQUIRES(mLock);
+
// Protect all internal state.
mutable std::mutex mLock;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 863cd41..b2b23e5 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -32,7 +32,11 @@
#include "InputReaderContext.h"
namespace android {
+// TODO b/180733860 support multiple battery in API and remove this.
+constexpr int32_t DEFAULT_BATTERY_ID = 1;
+class PeripheralController;
+class PeripheralControllerInterface;
class InputDeviceContext;
class InputMapper;
@@ -131,6 +135,20 @@
return *mapper;
}
+ // construct and add a controller to the input device
+ template <class T>
+ T& addController(int32_t eventHubId) {
+ // ensure a device entry exists for this eventHubId
+ addEventHubDevice(eventHubId, false);
+
+ // create controller
+ auto& devicePair = mDevices[eventHubId];
+ auto& deviceContext = devicePair.first;
+
+ mController = std::make_unique<T>(*deviceContext);
+ return *(reinterpret_cast<T*>(mController.get()));
+ }
+
private:
InputReaderContext* mContext;
int32_t mId;
@@ -143,7 +161,10 @@
// map from eventHubId to device context and mappers
using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
using DevicePair = std::pair<std::unique_ptr<InputDeviceContext>, MapperVector>;
+ // Map from EventHub ID to pair of device context and vector of mapper.
std::unordered_map<int32_t, DevicePair> mDevices;
+ // Misc devices controller for lights, battery, etc.
+ std::unique_ptr<PeripheralControllerInterface> mController;
uint32_t mSources;
bool mIsExternal;
@@ -319,11 +340,21 @@
inline std::vector<int32_t> getVibratorIds() { return mEventHub->getVibratorIds(mId); }
- inline std::optional<int32_t> getBatteryCapacity() {
- return mEventHub->getBatteryCapacity(mId);
+ inline const std::vector<int32_t> getRawBatteryIds() {
+ return mEventHub->getRawBatteryIds(mId);
}
- inline std::optional<int32_t> getBatteryStatus() { return mEventHub->getBatteryStatus(mId); }
+ inline std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t batteryId) {
+ return mEventHub->getRawBatteryInfo(mId, batteryId);
+ }
+
+ inline std::optional<int32_t> getBatteryCapacity(int32_t batteryId) {
+ return mEventHub->getBatteryCapacity(mId, batteryId);
+ }
+
+ inline std::optional<int32_t> getBatteryStatus(int32_t batteryId) {
+ return mEventHub->getBatteryStatus(mId, batteryId);
+ }
inline bool hasAbsoluteAxis(int32_t code) const {
RawAbsoluteAxisInfo info;
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
deleted file mode 100644
index e4fb3a6..0000000
--- a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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 "../Macros.h"
-
-#include "BatteryInputMapper.h"
-
-namespace android {
-
-BatteryInputMapper::BatteryInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext) {}
-
-uint32_t BatteryInputMapper::getSources() {
- return AINPUT_SOURCE_UNKNOWN;
-}
-
-void BatteryInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- info->setHasBattery(true);
-}
-
-void BatteryInputMapper::process(const RawEvent* rawEvent) {}
-
-std::optional<int32_t> BatteryInputMapper::getBatteryCapacity() {
- return getDeviceContext().getBatteryCapacity();
-}
-
-std::optional<int32_t> BatteryInputMapper::getBatteryStatus() {
- return getDeviceContext().getBatteryStatus();
-}
-
-void BatteryInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Battery Input Mapper:\n";
- dump += getBatteryCapacity().has_value()
- ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity().value())
- : StringPrintf(INDENT3 "Capacity: Unknown");
-
- std::string status;
- switch (getBatteryStatus().value_or(BATTERY_STATUS_UNKNOWN)) {
- case BATTERY_STATUS_CHARGING:
- status = "Charging";
- break;
- case BATTERY_STATUS_DISCHARGING:
- status = "Discharging";
- break;
- case BATTERY_STATUS_NOT_CHARGING:
- status = "Not charging";
- break;
- case BATTERY_STATUS_FULL:
- status = "Full";
- break;
- default:
- status = "Unknown";
- }
- dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
-}
-
-} // namespace android
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.h b/services/inputflinger/reader/mapper/BatteryInputMapper.h
deleted file mode 100644
index 4fe373e..0000000
--- a/services/inputflinger/reader/mapper/BatteryInputMapper.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
-#define _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
-
-#include "InputMapper.h"
-
-namespace android {
-
-class BatteryInputMapper : public InputMapper {
-public:
- explicit BatteryInputMapper(InputDeviceContext& deviceContext);
- virtual ~BatteryInputMapper(){};
-
- uint32_t getSources() override;
- void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
- void process(const RawEvent* rawEvent) override;
-
- std::optional<int32_t> getBatteryCapacity() override;
- std::optional<int32_t> getBatteryStatus() override;
-
- void dump(std::string& dump) override;
-};
-
-} // namespace android
-
-#endif // _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.cpp b/services/inputflinger/reader/mapper/LightInputMapper.cpp
deleted file mode 100644
index be1f722..0000000
--- a/services/inputflinger/reader/mapper/LightInputMapper.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <locale>
-#include <regex>
-
-#include "../Macros.h"
-
-#include "LightInputMapper.h"
-#include "input/NamedEnum.h"
-
-// Log detailed debug messages about input device lights.
-static constexpr bool DEBUG_LIGHT_DETAILS = false;
-
-namespace android {
-
-static inline int32_t getAlpha(int32_t color) {
- return (color >> 24) & 0xff;
-}
-
-static inline int32_t getRed(int32_t color) {
- return (color >> 16) & 0xff;
-}
-
-static inline int32_t getGreen(int32_t color) {
- return (color >> 8) & 0xff;
-}
-
-static inline int32_t getBlue(int32_t color) {
- return color & 0xff;
-}
-
-static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
- return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
-}
-
-/**
- * Light input mapper owned by InputReader device, implements the native API for querying input
- * lights, getting and setting the lights brightness and color, by interacting with EventHub
- * devices.
- * TODO b/180342233: Reconsider the inputflinger design to accommodate the device class
- * like lights and battery.
- */
-LightInputMapper::LightInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext) {}
-
-LightInputMapper::~LightInputMapper() {}
-
-std::optional<std::int32_t> LightInputMapper::Light::getRawLightBrightness(int32_t rawLightId) {
- std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
- std::optional<int32_t> ret = context.getLightBrightness(rawLightId);
- if (!rawInfo.has_value() || !ret.has_value()) {
- return std::nullopt;
- }
- int brightness = ret.value();
-
- // If the light node doesn't have max brightness, use the default max brightness.
- int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
- float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
- // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
- if (rawMaxBrightness != MAX_BRIGHTNESS) {
- brightness = brightness * ratio;
- }
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
- brightness, ratio);
- }
- return brightness;
-}
-
-void LightInputMapper::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
- std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
- if (!rawInfo.has_value()) {
- return;
- }
- // If the light node doesn't have max brightness, use the default max brightness.
- int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
- float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
- // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
- if (rawMaxBrightness != MAX_BRIGHTNESS) {
- brightness = ceil(brightness / ratio);
- }
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
- brightness, ratio);
- }
- context.setLightBrightness(rawLightId, brightness);
-}
-
-bool LightInputMapper::SingleLight::setLightColor(int32_t color) {
- int32_t brightness = getAlpha(color);
- setRawLightBrightness(rawId, brightness);
-
- return true;
-}
-
-bool LightInputMapper::RgbLight::setLightColor(int32_t color) {
- // Compose color value as per:
- // https://developer.android.com/reference/android/graphics/Color?hl=en
- // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
- // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
- // MAX_BRIGHTNESS.
- brightness = getAlpha(color);
- int32_t red = 0;
- int32_t green = 0;
- int32_t blue = 0;
- if (brightness > 0) {
- float ratio = MAX_BRIGHTNESS / brightness;
- red = ceil(getRed(color) / ratio);
- green = ceil(getGreen(color) / ratio);
- blue = ceil(getBlue(color) / ratio);
- }
- setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
- setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
- setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
- if (rawGlobalId.has_value()) {
- setRawLightBrightness(rawGlobalId.value(), brightness);
- }
-
- return true;
-}
-
-bool LightInputMapper::MultiColorLight::setLightColor(int32_t color) {
- std::unordered_map<LightColor, int32_t> intensities;
- intensities.emplace(LightColor::RED, getRed(color));
- intensities.emplace(LightColor::GREEN, getGreen(color));
- intensities.emplace(LightColor::BLUE, getBlue(color));
-
- context.setLightIntensities(rawId, intensities);
- setRawLightBrightness(rawId, getAlpha(color));
- return true;
-}
-
-std::optional<int32_t> LightInputMapper::SingleLight::getLightColor() {
- std::optional<int32_t> brightness = getRawLightBrightness(rawId);
- if (!brightness.has_value()) {
- return std::nullopt;
- }
-
- return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
-}
-
-std::optional<int32_t> LightInputMapper::RgbLight::getLightColor() {
- // If the Alpha component is zero, then return color 0.
- if (brightness == 0) {
- return 0;
- }
- // Compose color value as per:
- // https://developer.android.com/reference/android/graphics/Color?hl=en
- // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
- std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
- std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
- std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
- // If we can't get brightness for any of the RGB light
- if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
- return std::nullopt;
- }
-
- // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
- // value, scale it back to return the nominal color value.
- float ratio = MAX_BRIGHTNESS / brightness;
- int32_t red = round(redOr.value() * ratio);
- int32_t green = round(greenOr.value() * ratio);
- int32_t blue = round(blueOr.value() * ratio);
-
- if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
- // Previously stored brightness isn't valid for current LED values, so just reset to max
- // brightness since an app couldn't have provided these values in the first place.
- red = redOr.value();
- green = greenOr.value();
- blue = blueOr.value();
- brightness = MAX_BRIGHTNESS;
- }
-
- return toArgb(brightness, red, green, blue);
-}
-
-std::optional<int32_t> LightInputMapper::MultiColorLight::getLightColor() {
- auto ret = context.getLightIntensities(rawId);
- if (!ret.has_value()) {
- return std::nullopt;
- }
- std::unordered_map<LightColor, int32_t> intensities = ret.value();
- // Get red, green, blue colors
- int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
- intensities.at(LightColor::GREEN) /* green */,
- intensities.at(LightColor::BLUE) /* blue */);
- // Get brightness
- std::optional<int32_t> brightness = getRawLightBrightness(rawId);
- if (brightness.has_value()) {
- return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
- }
- return std::nullopt;
-}
-
-bool LightInputMapper::PlayerIdLight::setLightPlayerId(int32_t playerId) {
- if (rawLightIds.find(playerId) == rawLightIds.end()) {
- return false;
- }
- for (const auto& [id, rawId] : rawLightIds) {
- if (playerId == id) {
- setRawLightBrightness(rawId, MAX_BRIGHTNESS);
- } else {
- setRawLightBrightness(rawId, 0);
- }
- }
- return true;
-}
-
-std::optional<int32_t> LightInputMapper::PlayerIdLight::getLightPlayerId() {
- for (const auto& [id, rawId] : rawLightIds) {
- std::optional<int32_t> brightness = getRawLightBrightness(rawId);
- if (brightness.has_value() && brightness.value() > 0) {
- return id;
- }
- }
- return std::nullopt;
-}
-
-void LightInputMapper::SingleLight::dump(std::string& dump) {
- dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-}
-
-void LightInputMapper::PlayerIdLight::dump(std::string& dump) {
- dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
- dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
- for (const auto& [id, rawId] : rawLightIds) {
- dump += StringPrintf("id %d -> %d ", id, rawId);
- }
- dump += "\n";
-}
-
-void LightInputMapper::RgbLight::dump(std::string& dump) {
- dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
- dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
- rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
- if (rawGlobalId.has_value()) {
- dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
- }
- dump += "\n";
-}
-
-void LightInputMapper::MultiColorLight::dump(std::string& dump) {
- dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-}
-
-uint32_t LightInputMapper::getSources() {
- return AINPUT_SOURCE_UNKNOWN;
-}
-
-void LightInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
- InputMapper::populateDeviceInfo(info);
-
- for (const auto& [lightId, light] : mLights) {
- // Input device light doesn't support ordinal, always pass 1.
- InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
- info->addLightInfo(lightInfo);
- }
-}
-
-void LightInputMapper::dump(std::string& dump) {
- dump += INDENT2 "Light Input Mapper:\n";
- dump += INDENT3 "Lights:\n";
- for (const auto& [lightId, light] : mLights) {
- dump += StringPrintf(INDENT4 "Id: %d", lightId);
- dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
- dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
- light->dump(dump);
- }
- // Dump raw lights
- dump += INDENT3 "RawLights:\n";
- dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
- const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
- // Map from raw light id to raw light info
- std::unordered_map<int32_t, RawLightInfo> rawInfos;
- for (const auto& rawId : rawLightIds) {
- std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
- if (!rawInfo.has_value()) {
- continue;
- }
- dump += StringPrintf(INDENT4 "%d", rawId);
- dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
- dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
- dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
- dump += StringPrintf(INDENT4 "%d\n",
- getDeviceContext().getLightBrightness(rawId).value_or(-1));
- }
-}
-
-void LightInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
-
- if (!changes) { // first time only
- bool hasRedLed = false;
- bool hasGreenLed = false;
- bool hasBlueLed = false;
- std::optional<int32_t> rawGlobalId = std::nullopt;
- // Player ID light common name string
- std::string playerIdName;
- // Raw RGB color to raw light ID
- std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
- // Map from player Id to raw light Id
- std::unordered_map<int32_t, int32_t> playerIdLightIds;
- mLights.clear();
-
- // Check raw lights
- const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
- // Map from raw light id to raw light info
- std::unordered_map<int32_t, RawLightInfo> rawInfos;
- for (const auto& rawId : rawLightIds) {
- std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
- if (!rawInfo.has_value()) {
- continue;
- }
- rawInfos.insert_or_assign(rawId, rawInfo.value());
- // Check if this is a group LEDs for player ID
- std::regex lightPattern("([a-z]+)([0-9]+)");
- std::smatch results;
- if (std::regex_match(rawInfo->name, results, lightPattern)) {
- std::string commonName = results[1].str();
- int32_t playerId = std::stoi(results[2]);
- if (playerIdLightIds.empty()) {
- playerIdName = commonName;
- playerIdLightIds.insert_or_assign(playerId, rawId);
- } else {
- // Make sure the player ID leds have common string name
- if (playerIdName.compare(commonName) == 0 &&
- playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
- playerIdLightIds.insert_or_assign(playerId, rawId);
- }
- }
- }
- // Check if this is an LED of RGB light
- if (rawInfo->flags.test(InputLightClass::RED)) {
- hasRedLed = true;
- rawRgbIds.emplace(LightColor::RED, rawId);
- }
- if (rawInfo->flags.test(InputLightClass::GREEN)) {
- hasGreenLed = true;
- rawRgbIds.emplace(LightColor::GREEN, rawId);
- }
- if (rawInfo->flags.test(InputLightClass::BLUE)) {
- hasBlueLed = true;
- rawRgbIds.emplace(LightColor::BLUE, rawId);
- }
- if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
- rawGlobalId = rawId;
- }
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id,
- rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
- rawInfo->flags.string().c_str());
- }
- }
-
- // Construct a player ID light
- if (playerIdLightIds.size() > 1) {
- std::unique_ptr<Light> light =
- std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
- playerIdLightIds);
- mLights.insert_or_assign(light->id, std::move(light));
- // Remove these raw lights from raw light info as they've been used to compose a
- // Player ID light, so we do not expose these raw lights as single lights.
- for (const auto& [playerId, rawId] : playerIdLightIds) {
- rawInfos.erase(rawId);
- }
- }
- // Construct a RGB light for composed RGB light
- if (hasRedLed && hasGreenLed && hasBlueLed) {
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
- rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
- }
- std::unique_ptr<Light> light = std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
- rawRgbIds, rawGlobalId);
- mLights.insert_or_assign(light->id, std::move(light));
- // Remove from raw light info as they've been composed a RBG light.
- rawInfos.erase(rawRgbIds.at(LightColor::RED));
- rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
- rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
- if (rawGlobalId.has_value()) {
- rawInfos.erase(rawGlobalId.value());
- }
- }
-
- // Check the rest of raw light infos
- for (const auto& [rawId, rawInfo] : rawInfos) {
- // If the node is multi-color led, construct a MULTI_COLOR light
- if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
- rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
- }
- std::unique_ptr<Light> light =
- std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name,
- ++mNextId, rawInfo.id);
- mLights.insert_or_assign(light->id, std::move(light));
- continue;
- }
- // Construct a single LED light
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
- }
- std::unique_ptr<Light> light =
- std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
- rawInfo.id);
-
- mLights.insert_or_assign(light->id, std::move(light));
- }
- }
-}
-
-void LightInputMapper::reset(nsecs_t when) {
- InputMapper::reset(when);
-}
-
-void LightInputMapper::process(const RawEvent* rawEvent) {}
-
-bool LightInputMapper::setLightColor(int32_t lightId, int32_t color) {
- auto it = mLights.find(lightId);
- if (it == mLights.end()) {
- return false;
- }
- auto& light = it->second;
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
- NamedEnum::string(light->type).c_str(), color);
- }
- return light->setLightColor(color);
-}
-
-std::optional<int32_t> LightInputMapper::getLightColor(int32_t lightId) {
- auto it = mLights.find(lightId);
- if (it == mLights.end()) {
- return std::nullopt;
- }
- auto& light = it->second;
- std::optional<int32_t> color = light->getLightColor();
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
- NamedEnum::string(light->type).c_str(), color.value_or(0));
- }
- return color;
-}
-
-bool LightInputMapper::setLightPlayerId(int32_t lightId, int32_t playerId) {
- auto it = mLights.find(lightId);
- if (it == mLights.end()) {
- return false;
- }
- auto& light = it->second;
- return light->setLightPlayerId(playerId);
-}
-
-std::optional<int32_t> LightInputMapper::getLightPlayerId(int32_t lightId) {
- auto it = mLights.find(lightId);
- if (it == mLights.end()) {
- return std::nullopt;
- }
- auto& light = it->second;
- return light->getLightPlayerId();
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index cedda6e..31d6900 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -268,7 +268,9 @@
void assertDropTargetEquals(const sp<IBinder>& targetToken) {
std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mNotifyDropWindowWasCalled);
ASSERT_EQ(targetToken, mDropTargetWindowToken);
+ mNotifyDropWindowWasCalled = false;
}
private:
@@ -290,6 +292,7 @@
std::condition_variable mNotifyAnr;
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
+ bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
@@ -386,7 +389,9 @@
void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
- bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
+ bool checkInjectEventsPermissionNonReentrant(int32_t pid, int32_t uid) override {
+ return pid == INJECTOR_PID && uid == INJECTOR_UID;
+ }
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
@@ -401,6 +406,7 @@
void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
std::scoped_lock lock(mLock);
+ mNotifyDropWindowWasCalled = true;
mDropTargetWindowToken = token;
}
@@ -740,6 +746,11 @@
ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+ ASSERT_EQ(OK, status);
+ }
+
void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
@@ -1052,6 +1063,11 @@
mInputReceiver->finishEvent(sequenceNum);
}
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->sendTimeline(inputEventId, timeline);
+ }
+
InputEvent* consume() {
if (mInputReceiver == nullptr) {
return nullptr;
@@ -1090,7 +1106,8 @@
const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
- std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ bool allowKeyRepeat = true) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1099,10 +1116,13 @@
INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
repeatCount, currentTime, currentTime);
+ int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+ if (!allowKeyRepeat) {
+ policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+ }
// Inject event until dispatch out.
return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
- injectionTimeout,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ injectionTimeout, policyFlags);
}
static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -1110,6 +1130,16 @@
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
}
+// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
+// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
+// has to be woken up to process the repeating key.
+static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId,
+ InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
+ /* allowKeyRepeat */ false);
+}
+
static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
@@ -1165,8 +1195,8 @@
return *this;
}
- MotionEventBuilder& buttonState(int32_t actionButton) {
- mActionButton = actionButton;
+ MotionEventBuilder& buttonState(int32_t buttonState) {
+ mButtonState = buttonState;
return *this;
}
@@ -1254,7 +1284,7 @@
.build();
// Inject event until dispatch out.
- return injectMotionEvent(dispatcher, event);
+ return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
}
static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
@@ -1954,6 +1984,21 @@
secondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
+
+ window->sendTimeline(1 /*inputEventId*/, graphicsTimeline);
+ window->assertNoEvents();
+ mDispatcher->waitForIdle();
+}
+
class FakeMonitorReceiver {
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
@@ -2749,13 +2794,14 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
@@ -2768,7 +2814,7 @@
AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeFocusEvent(false);
@@ -2990,7 +3036,8 @@
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -3271,7 +3318,7 @@
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -3284,7 +3331,8 @@
InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/,
+ false /* allowKeyRepeat */);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not go to window because we have no focused window.
// The 'no focused window' ANR timer should start instead.
@@ -3320,7 +3368,7 @@
// Send a key to the app and have the app not respond right away.
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -3347,7 +3395,7 @@
// injection times out (instead of failing).
const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
@@ -3366,7 +3414,7 @@
// injection times out (instead of failing).
const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -3955,7 +4003,8 @@
// Key will not be sent anywhere because we have no focused window. It will remain pending.
InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/,
+ false /* allowKeyRepeat */);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
@@ -4761,6 +4810,32 @@
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) {
@@ -4833,4 +4908,87 @@
mSecondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
+ performStylusDrag();
+
+ // Move on window and keep button pressed.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(50)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window and release button, expect to drop item.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+
+ // nothing to the window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) {
+ performDrag();
+
+ // Set second window invisible.
+ mSecondWindow->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->assertNoEvents();
+
+ // drop to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mFakePolicy->assertDropTargetEquals(nullptr);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d69bb6a..0e721e9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <BatteryInputMapper.h>
#include <CursorInputMapper.h>
#include <InputDevice.h>
#include <InputMapper.h>
@@ -22,8 +21,8 @@
#include <InputReaderBase.h>
#include <InputReaderFactory.h>
#include <KeyboardInputMapper.h>
-#include <LightInputMapper.h>
#include <MultiTouchInputMapper.h>
+#include <PeripheralController.h>
#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
#include <SwitchInputMapper.h>
@@ -71,6 +70,7 @@
static constexpr int32_t FIRST_TRACKING_ID = 0;
static constexpr int32_t SECOND_TRACKING_ID = 1;
static constexpr int32_t THIRD_TRACKING_ID = 2;
+static constexpr int32_t DEFAULT_BATTERY = 1;
static constexpr int32_t BATTERY_STATUS = 4;
static constexpr int32_t BATTERY_CAPACITY = 66;
static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
@@ -895,9 +895,19 @@
std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
- std::optional<int32_t> getBatteryCapacity(int32_t) const override { return BATTERY_CAPACITY; }
+ std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
+ return BATTERY_CAPACITY;
+ }
- std::optional<int32_t> getBatteryStatus(int32_t) const override { return BATTERY_STATUS; }
+ std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
+ return BATTERY_STATUS;
+ }
+
+ const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; }
+
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+ return std::nullopt;
+ }
const std::vector<int32_t> getRawLightIds(int32_t deviceId) override {
std::vector<int32_t> ids;
@@ -2005,56 +2015,25 @@
ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
}
-class FakeBatteryInputMapper : public FakeInputMapper {
-public:
- FakeBatteryInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
- : FakeInputMapper(deviceContext, sources) {}
+// --- FakePeripheralController ---
- std::optional<int32_t> getBatteryCapacity() override {
- return getDeviceContext().getBatteryCapacity();
+class FakePeripheralController : public PeripheralControllerInterface {
+public:
+ FakePeripheralController(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
+
+ ~FakePeripheralController() override {}
+
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
+
+ void dump(std::string& dump) override {}
+
+ std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override {
+ return getDeviceContext().getBatteryCapacity(batteryId);
}
- std::optional<int32_t> getBatteryStatus() override {
- return getDeviceContext().getBatteryStatus();
+ std::optional<int32_t> getBatteryStatus(int32_t batteryId) override {
+ return getDeviceContext().getBatteryStatus(batteryId);
}
-};
-
-TEST_F(InputReaderTest, BatteryGetCapacity) {
- constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
- constexpr int32_t eventHubId = 1;
- const char* DEVICE_LOCATION = "BLUETOOTH";
- std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
- FakeBatteryInputMapper& mapper =
- device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->pushNextDevice(device);
-
- ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
-
- ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
-}
-
-TEST_F(InputReaderTest, BatteryGetStatus) {
- constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
- constexpr int32_t eventHubId = 1;
- const char* DEVICE_LOCATION = "BLUETOOTH";
- std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
- FakeBatteryInputMapper& mapper =
- device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->pushNextDevice(device);
-
- ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
-
- ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
-}
-
-class FakeLightInputMapper : public FakeInputMapper {
-public:
- FakeLightInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
- : FakeInputMapper(deviceContext, sources) {}
bool setLightColor(int32_t lightId, int32_t color) override {
getDeviceContext().setLightBrightness(lightId, color >> 24);
@@ -2068,16 +2047,57 @@
}
return result.value() << 24;
}
+
+ bool setLightPlayerId(int32_t lightId, int32_t playerId) override { return true; }
+
+ std::optional<int32_t> getLightPlayerId(int32_t lightId) override { return std::nullopt; }
+
+private:
+ InputDeviceContext& mDeviceContext;
+ inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+ inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
};
+TEST_F(InputReaderTest, BatteryGetCapacity) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakePeripheralController& controller =
+ device->addController<FakePeripheralController>(eventHubId);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+ ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
+ ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+}
+
+TEST_F(InputReaderTest, BatteryGetStatus) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakePeripheralController& controller =
+ device->addController<FakePeripheralController>(eventHubId);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+ ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
+ ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+}
+
TEST_F(InputReaderTest, LightGetColor) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT;
constexpr int32_t eventHubId = 1;
const char* DEVICE_LOCATION = "BLUETOOTH";
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
- FakeLightInputMapper& mapper =
- device->addMapper<FakeLightInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+ FakePeripheralController& controller =
+ device->addController<FakePeripheralController>(eventHubId);
mReader->pushNextDevice(device);
RawLightInfo info = {.id = 1,
.name = "Mono",
@@ -2088,8 +2108,9 @@
mFakeEventHub->fakeLightBrightness(1 /* rawId */, 0x55);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+ ASSERT_TRUE(controller.setLightColor(1 /* lightId */, LIGHT_BRIGHTNESS));
+ ASSERT_EQ(controller.getLightColor(1 /* lightId */), LIGHT_BRIGHTNESS);
ASSERT_TRUE(mReader->setLightColor(deviceId, 1 /* lightId */, LIGHT_BRIGHTNESS));
ASSERT_EQ(mReader->getLightColor(deviceId, 1 /* lightId */), LIGHT_BRIGHTNESS);
}
@@ -2989,154 +3010,6 @@
mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
}
-// --- BatteryInputMapperTest ---
-class BatteryInputMapperTest : public InputMapperTest {
-protected:
- void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY); }
-};
-
-TEST_F(BatteryInputMapperTest, GetSources) {
- BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(BatteryInputMapperTest, GetBatteryCapacity) {
- BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
- ASSERT_TRUE(mapper.getBatteryCapacity());
- ASSERT_EQ(mapper.getBatteryCapacity().value_or(-1), BATTERY_CAPACITY);
-}
-
-TEST_F(BatteryInputMapperTest, GetBatteryStatus) {
- BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
- ASSERT_TRUE(mapper.getBatteryStatus());
- ASSERT_EQ(mapper.getBatteryStatus().value_or(-1), BATTERY_STATUS);
-}
-
-// --- LightInputMapperTest ---
-class LightInputMapperTest : public InputMapperTest {
-protected:
- void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); }
-};
-
-TEST_F(LightInputMapperTest, GetSources) {
- LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(LightInputMapperTest, SingleLight) {
- RawLightInfo infoSingle = {.id = 1,
- .name = "Mono",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS,
- .path = ""};
- mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
-
- LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
-
- ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_BRIGHTNESS));
- ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
-}
-
-TEST_F(LightInputMapperTest, RGBLight) {
- RawLightInfo infoRed = {.id = 1,
- .name = "red",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
- .path = ""};
- RawLightInfo infoGreen = {.id = 2,
- .name = "green",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
- .path = ""};
- RawLightInfo infoBlue = {.id = 3,
- .name = "blue",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
- .path = ""};
- mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
- mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
- mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
-
- LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
-
- ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR));
- ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
-}
-
-TEST_F(LightInputMapperTest, MultiColorRGBLight) {
- RawLightInfo infoColor = {.id = 1,
- .name = "red",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS |
- InputLightClass::MULTI_INTENSITY |
- InputLightClass::MULTI_INDEX,
- .path = ""};
-
- mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
-
- LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
-
- ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR));
- ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
-}
-
-TEST_F(LightInputMapperTest, PlayerIdLight) {
- RawLightInfo info1 = {.id = 1,
- .name = "player1",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS,
- .path = ""};
- RawLightInfo info2 = {.id = 2,
- .name = "player2",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS,
- .path = ""};
- RawLightInfo info3 = {.id = 3,
- .name = "player3",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS,
- .path = ""};
- RawLightInfo info4 = {.id = 4,
- .name = "player4",
- .maxBrightness = 255,
- .flags = InputLightClass::BRIGHTNESS,
- .path = ""};
- mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
- mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
- mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
- mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
-
- LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
- InputDeviceInfo info;
- mapper.populateDeviceInfo(&info);
- const auto& ids = info.getLightIds();
- ASSERT_EQ(1UL, ids.size());
- ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
-
- ASSERT_FALSE(mapper.setLightColor(ids[0], LIGHT_COLOR));
- ASSERT_TRUE(mapper.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
- ASSERT_EQ(mapper.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
-}
-
// --- KeyboardInputMapperTest ---
class KeyboardInputMapperTest : public InputMapperTest {
@@ -8728,4 +8601,218 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
+// --- PeripheralControllerTest ---
+
+class PeripheralControllerTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
+ static const int32_t DEVICE_ID;
+ static const int32_t DEVICE_GENERATION;
+ static const int32_t DEVICE_CONTROLLER_NUMBER;
+ static const Flags<InputDeviceClass> DEVICE_CLASSES;
+ static const int32_t EVENTHUB_ID;
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ sp<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(Flags<InputDeviceClass> classes) {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = new FakeInputReaderPolicy();
+ mFakeListener = new TestInputListener();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+ }
+
+ void SetUp() override { SetUp(DEVICE_CLASSES); }
+
+ void TearDown() override {
+ mFakeListener.clear();
+ mFakePolicy.clear();
+ }
+
+ void configureDevice(uint32_t changes) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ }
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ Flags<InputDeviceClass> classes) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes);
+ mReader->loopOnce();
+ return device;
+ }
+
+ template <class T, typename... Args>
+ T& addControllerAndConfigure(Args... args) {
+ T& controller = mDevice->addController<T>(EVENTHUB_ID, args...);
+
+ return controller;
+ }
+};
+
+const char* PeripheralControllerTest::DEVICE_NAME = "device";
+const char* PeripheralControllerTest::DEVICE_LOCATION = "BLUETOOTH";
+const int32_t PeripheralControllerTest::DEVICE_ID = END_RESERVED_ID + 1000;
+const int32_t PeripheralControllerTest::DEVICE_GENERATION = 2;
+const int32_t PeripheralControllerTest::DEVICE_CONTROLLER_NUMBER = 0;
+const Flags<InputDeviceClass> PeripheralControllerTest::DEVICE_CLASSES =
+ Flags<InputDeviceClass>(0); // not needed for current tests
+const int32_t PeripheralControllerTest::EVENTHUB_ID = 1;
+
+// --- BatteryControllerTest ---
+class BatteryControllerTest : public PeripheralControllerTest {
+protected:
+ void SetUp() override {
+ PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY);
+ }
+};
+
+TEST_F(BatteryControllerTest, GetBatteryCapacity) {
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+
+ ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+}
+
+TEST_F(BatteryControllerTest, GetBatteryStatus) {
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+
+ ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+}
+
+// --- LightControllerTest ---
+class LightControllerTest : public PeripheralControllerTest {
+protected:
+ void SetUp() override {
+ PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT);
+ }
+};
+
+TEST_F(LightControllerTest, SingleLight) {
+ RawLightInfo infoSingle = {.id = 1,
+ .name = "Mono",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
+
+ ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS));
+ ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, RGBLight) {
+ RawLightInfo infoRed = {.id = 1,
+ .name = "red",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+ .path = ""};
+ RawLightInfo infoGreen = {.id = 2,
+ .name = "green",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+ .path = ""};
+ RawLightInfo infoBlue = {.id = 3,
+ .name = "blue",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+ mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+ mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
+
+ ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+ ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBLight) {
+ RawLightInfo infoColor = {.id = 1,
+ .name = "red",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS |
+ InputLightClass::MULTI_INTENSITY |
+ InputLightClass::MULTI_INDEX,
+ .path = ""};
+
+ mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
+
+ ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+ ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, PlayerIdLight) {
+ RawLightInfo info1 = {.id = 1,
+ .name = "player1",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info2 = {.id = 2,
+ .name = "player2",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info3 = {.id = 3,
+ .name = "player3",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info4 = {.id = 4,
+ .name = "player4",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
+ mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
+ mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
+ mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ const auto& ids = info.getLightIds();
+ ASSERT_EQ(1UL, ids.size());
+ ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
+
+ ASSERT_FALSE(controller.setLightColor(ids[0], LIGHT_COLOR));
+ ASSERT_TRUE(controller.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
+ ASSERT_EQ(controller.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
+}
+
} // namespace android
diff --git a/services/memtrackproxy/Android.bp b/services/memtrackproxy/Android.bp
new file mode 100644
index 0000000..7d78f3b
--- /dev/null
+++ b/services/memtrackproxy/Android.bp
@@ -0,0 +1,50 @@
+// 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.
+
+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: "libmemtrackproxy",
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libcutils",
+ "libutils",
+ "android.hardware.memtrack@1.0",
+ "android.hardware.memtrack-V1-ndk_platform",
+ ],
+ srcs: [
+ "MemtrackProxy.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ local_include_dirs: [
+ "include/memtrackproxy",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.memtrack@1.0",
+ "android.hardware.memtrack-V1-ndk_platform",
+ ],
+}
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
new file mode 100644
index 0000000..4676167
--- /dev/null
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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 "MemtrackProxy.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <private/android_filesystem_config.h>
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+// Check Memtrack Flags
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_ACCOUNTED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_ACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_UNACCOUNTED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_UNACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED_PSS) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED_PSS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::PRIVATE) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_PRIVATE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SYSTEM) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SYSTEM));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::DEDICATED) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_DEDICATED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::NONSECURE) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_NONSECURE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SECURE) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SECURE));
+
+// Check Memtrack Types
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::OTHER) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::OTHER));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GL) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::GL));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GRAPHICS) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::GRAPHICS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::MULTIMEDIA) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::MULTIMEDIA));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::CAMERA) ==
+ static_cast<uint32_t>(V1_aidl::MemtrackType::CAMERA));
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+ V1_aidl::MemtrackRecord* out) {
+ // Convert uint64_t to int64_t (long in AIDL). AIDL doesn't support unsigned types.
+ if (in.sizeInBytes > std::numeric_limits<int64_t>::max() || in.sizeInBytes < 0) {
+ return false;
+ }
+ out->sizeInBytes = static_cast<int64_t>(in.sizeInBytes);
+
+ // It's ok to just assign directly, since this is a bitmap.
+ out->flags = in.flags;
+ return true;
+}
+
+sp<V1_0_hidl::IMemtrack> MemtrackProxy::MemtrackHidlInstance() {
+ return V1_0_hidl::IMemtrack::getService();
+}
+
+std::shared_ptr<V1_aidl::IMemtrack> MemtrackProxy::MemtrackAidlInstance() {
+ const auto instance = std::string() + V1_aidl::IMemtrack::descriptor + "/default";
+ bool declared = AServiceManager_isDeclared(instance.c_str());
+ if (!declared) {
+ return nullptr;
+ }
+ ndk::SpAIBinder memtrack_binder =
+ ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()));
+ return V1_aidl::IMemtrack::fromBinder(memtrack_binder);
+}
+
+bool MemtrackProxy::CheckUid(uid_t calling_uid) {
+ // Allow AID_SHELL for adb shell dumpsys meminfo
+ return calling_uid == AID_SYSTEM || calling_uid == AID_ROOT || calling_uid == AID_SHELL;
+}
+
+bool MemtrackProxy::CheckPid(pid_t calling_pid, pid_t request_pid) {
+ return calling_pid == request_pid;
+}
+
+MemtrackProxy::MemtrackProxy()
+ : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
+ memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+
+ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
+ std::vector<MemtrackRecord>* _aidl_return) {
+ if (pid < 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (!MemtrackProxy::CheckPid(AIBinder_getCallingPid(), pid) &&
+ !MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_SECURITY,
+ "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getMemory() for PIDs other "
+ "than the calling PID");
+ }
+
+ if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
+ type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ _aidl_return->clear();
+
+ if (memtrack_aidl_instance_) {
+ return memtrack_aidl_instance_->getMemory(pid, type, _aidl_return);
+ } else if (memtrack_hidl_instance_) {
+ ndk::ScopedAStatus aidl_status;
+
+ Return<void> ret = memtrack_hidl_instance_->getMemory(
+ pid, static_cast<V1_0_hidl::MemtrackType>(type),
+ [&_aidl_return, &aidl_status](V1_0_hidl::MemtrackStatus status,
+ hidl_vec<V1_0_hidl::MemtrackRecord> records) {
+ switch (status) {
+ case V1_0_hidl::MemtrackStatus::SUCCESS:
+ aidl_status = ndk::ScopedAStatus::ok();
+ break;
+ case V1_0_hidl::MemtrackStatus::MEMORY_TRACKING_NOT_SUPPORTED:
+ [[fallthrough]];
+ case V1_0_hidl::MemtrackStatus::TYPE_NOT_SUPPORTED:
+ [[fallthrough]];
+ default:
+ aidl_status =
+ ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ return;
+ }
+
+ _aidl_return->resize(records.size());
+ for (size_t i = 0; i < records.size(); i++) {
+ if (!translate(records[i], &(*_aidl_return)[i])) {
+ aidl_status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_SERVICE_SPECIFIC,
+ "Failed to convert HIDL MemtrackRecord to AIDL");
+ return;
+ }
+ }
+ });
+
+ // Check HIDL return
+ if (!ret.isOk()) {
+ const char* err_msg = "HIDL Memtrack::getMemory() failed";
+ aidl_status =
+ ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC, err_msg);
+ LOG(ERROR) << err_msg << ": " << ret.description();
+ }
+
+ return aidl_status;
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+ "Memtrack HAL service not available");
+}
+
+ndk::ScopedAStatus MemtrackProxy::getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) {
+ if (!MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SECURITY,
+ "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getGpuDeviceInfo()");
+ }
+
+ _aidl_return->clear();
+
+ if (memtrack_aidl_instance_ ||
+ (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+ return memtrack_aidl_instance_->getGpuDeviceInfo(_aidl_return);
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+ "Memtrack HAL service not available");
+}
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
new file mode 100644
index 0000000..5ac1fbf
--- /dev/null
+++ b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/memtrack/BnMemtrack.h>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/hardware/memtrack/1.0/IMemtrack.h>
+
+using ::android::sp;
+
+namespace V1_0_hidl = ::android::hardware::memtrack::V1_0;
+namespace V1_aidl = ::aidl::android::hardware::memtrack;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+ V1_aidl::MemtrackRecord* out);
+
+class MemtrackProxy : public BnMemtrack {
+public:
+ MemtrackProxy();
+ ndk::ScopedAStatus getMemory(int pid, MemtrackType type,
+ std::vector<MemtrackRecord>* _aidl_return) override;
+ ndk::ScopedAStatus getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) override;
+
+private:
+ static sp<V1_0_hidl::IMemtrack> MemtrackHidlInstance();
+ static std::shared_ptr<V1_aidl::IMemtrack> MemtrackAidlInstance();
+ static bool CheckUid(uid_t calling_uid);
+ static bool CheckPid(pid_t calling_pid, pid_t request_pid);
+
+ sp<V1_0_hidl::IMemtrack> memtrack_hidl_instance_;
+ std::shared_ptr<V1_aidl::IMemtrack> memtrack_aidl_instance_;
+};
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/test/Android.bp b/services/memtrackproxy/test/Android.bp
new file mode 100644
index 0000000..f943761
--- /dev/null
+++ b/services/memtrackproxy/test/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+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: "memtrackproxy_test",
+ srcs: [
+ "MemtrackProxyTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libmemtrackproxy",
+ "android.hardware.memtrack-V1-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
diff --git a/services/memtrackproxy/test/MemtrackProxyTest.cpp b/services/memtrackproxy/test/MemtrackProxyTest.cpp
new file mode 100644
index 0000000..16dfba0
--- /dev/null
+++ b/services/memtrackproxy/test/MemtrackProxyTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+using aidl::android::hardware::memtrack::DeviceInfo;
+using aidl::android::hardware::memtrack::IMemtrack;
+using aidl::android::hardware::memtrack::MemtrackRecord;
+using aidl::android::hardware::memtrack::MemtrackType;
+
+class MemtrackProxyTest : public ::testing::Test {
+public:
+ virtual void SetUp() override {
+ const char* kMemtrackProxyService = "memtrack.proxy";
+ auto memtrackProxyBinder =
+ ndk::SpAIBinder(AServiceManager_waitForService(kMemtrackProxyService));
+ memtrack_proxy_ = IMemtrack::fromBinder(memtrackProxyBinder);
+ ASSERT_NE(memtrack_proxy_, nullptr);
+ }
+
+ std::shared_ptr<IMemtrack> memtrack_proxy_;
+};
+
+TEST_F(MemtrackProxyTest, GetMemoryForInvalidPid) {
+ int pid = -1;
+
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+ EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForCallingPid) {
+ int pid = getpid();
+
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+ EXPECT_TRUE(status.isOk());
+ }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForOtherPid) {
+ int pid = 1;
+
+ for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+ std::vector<MemtrackRecord> records;
+
+ auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+ // Test is run as root
+ EXPECT_TRUE(status.isOk());
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 1d3e5b5..d828aa9 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -38,7 +38,7 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V1-cpp",
+ "android.hardware.power-V2-cpp",
],
cflags: [
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 178f545..8c225d5 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -18,6 +18,7 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalController.h>
#include <powermanager/PowerHalLoader.h>
@@ -73,9 +74,10 @@
// Check if a call to Power HAL function failed; if so, log the failure and
// invalidate the current Power HAL handle.
-HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) {
- if (result == HalResult::FAILED) {
- ALOGE("%s() failed: power HAL service not available.", fnName);
+template <typename T>
+HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const char* fnName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: %s", fnName, result.errorMessage());
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
// Drop Power HAL handle. This will force future api calls to reconnect.
mConnectedHal = nullptr;
@@ -84,18 +86,31 @@
return result;
}
-HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->setBoost(boost, durationMs);
return processHalResult(result, "setBoost");
}
-HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->setMode(mode, enabled);
return processHalResult(result, "setMode");
}
+HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
+ return processHalResult(result, "createHintSession");
+}
+
+HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->getHintSessionPreferredRate();
+ return processHalResult(result, "getHintSessionPreferredRate");
+}
+
} // namespace power
} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 2f32827..d74bd23 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -16,11 +16,17 @@
#define LOG_TAG "HalWrapper"
#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
+#include <cinttypes>
+
using namespace android::hardware::power;
+namespace V1_0 = android::hardware::power::V1_0;
+namespace V1_1 = android::hardware::power::V1_1;
+namespace Aidl = android::hardware::power;
namespace android {
@@ -28,49 +34,88 @@
// -------------------------------------------------------------------------------------------------
-inline HalResult toHalResult(const binder::Status& result) {
+inline HalResult<void> toHalResult(const binder::Status& result) {
if (result.isOk()) {
- return HalResult::SUCCESSFUL;
+ return HalResult<void>::ok();
}
ALOGE("Power HAL request failed: %s", result.toString8().c_str());
- return HalResult::FAILED;
+ return HalResult<void>::fromStatus(result);
}
template <typename T>
-inline HalResult toHalResult(const hardware::Return<T>& result) {
- if (result.isOk()) {
- return HalResult::SUCCESSFUL;
- }
- ALOGE("Power HAL request failed: %s", result.description().c_str());
- return HalResult::FAILED;
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
}
// -------------------------------------------------------------------------------------------------
-HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+ if (status == android::OK) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(statusToString(status));
+}
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+}
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
toString(boost).c_str(), durationMs);
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
-HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) {
ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
enabled ? "true" : "false");
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+ int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
+ ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
+ threadIds.size());
+ return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
+ ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+ return HalResult<int64_t>::unsupported();
}
// -------------------------------------------------------------------------------------------------
-HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
if (boost == Boost::INTERACTION) {
return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
} else {
ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
}
-HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
switch (mode) {
case Mode::LAUNCH:
@@ -88,38 +133,54 @@
default:
ALOGV("Skipped setMode %s because Power HAL AIDL not available",
toString(mode).c_str());
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
}
-HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
- return toHalResult(mHandleV1_0->powerHint(hintId, data));
+HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ auto ret = mHandleV1_0->powerHint(hintId, data);
+ return HalResult<void>::fromReturn(ret);
}
-HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
- return toHalResult(mHandleV1_0->setInteractive(enabled));
+HalResult<void> HidlHalWrapperV1_0::setInteractive(bool enabled) {
+ auto ret = mHandleV1_0->setInteractive(enabled);
+ return HalResult<void>::fromReturn(ret);
}
-HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
- return toHalResult(mHandleV1_0->setFeature(feature, enabled));
+HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+ auto ret = mHandleV1_0->setFeature(feature, enabled);
+ return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+ int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
+ ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
+ threadIds.size());
+ return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
+ ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+ return HalResult<int64_t>::unsupported();
}
// -------------------------------------------------------------------------------------------------
-HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
- return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
+HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ auto ret = mHandleV1_1->powerHintAsync(hintId, data);
+ return HalResult<void>::fromReturn(ret);
}
// -------------------------------------------------------------------------------------------------
-HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
std::unique_lock<std::mutex> lock(mBoostMutex);
size_t idx = static_cast<size_t>(boost);
// Quick return if boost is not supported by HAL
if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
if (mBoostSupportedArray[idx] == HalSupport::UNKNOWN) {
@@ -128,14 +189,15 @@
if (!isSupportedRet.isOk()) {
ALOGE("Skipped setBoost %s because check support failed with: %s",
toString(boost).c_str(), isSupportedRet.toString8().c_str());
- return HalResult::FAILED;
+ // return HalResult::FAILED;
+ return HalResult<void>::fromStatus(isSupportedRet);
}
mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
toString(boost).c_str());
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
}
lock.unlock();
@@ -143,30 +205,28 @@
return toHalResult(mHandle->setBoost(boost, durationMs));
}
-HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) {
std::unique_lock<std::mutex> lock(mModeMutex);
size_t idx = static_cast<size_t>(mode);
// Quick return if mode is not supported by HAL
if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
if (mModeSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
if (!isSupportedRet.isOk()) {
- ALOGE("Skipped setMode %s because check support failed with: %s",
- toString(mode).c_str(), isSupportedRet.toString8().c_str());
- return HalResult::FAILED;
+ return HalResult<void>::failed(isSupportedRet.toString8().c_str());
}
mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setMode %s because Power HAL doesn't support it",
toString(mode).c_str());
- return HalResult::UNSUPPORTED;
+ return HalResult<void>::unsupported();
}
}
lock.unlock();
@@ -174,6 +234,20 @@
return toHalResult(mHandle->setMode(mode, enabled));
}
+HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+ sp<IPowerHintSession> appSession;
+ return HalResult<sp<Aidl::IPowerHintSession>>::
+ fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
+ appSession);
+}
+
+HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() {
+ int64_t rate = -1;
+ auto result = mHandle->getHintSessionPreferredRate(&rate);
+ return HalResult<int64_t>::fromStatus(result, rate);
+}
+
// -------------------------------------------------------------------------------------------------
} // namespace power
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index a489253..3997929 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -38,7 +38,7 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V1-cpp",
+ "android.hardware.power-V2-cpp",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 1004828..1100cad 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -18,7 +18,9 @@
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/Mode.h>
+#include <android/hardware/power/WorkDuration.h>
#include <benchmark/benchmark.h>
#include <binder/IServiceManager.h>
#include <testUtil.h>
@@ -26,7 +28,9 @@
using android::hardware::power::Boost;
using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
+using android::hardware::power::WorkDuration;
using std::chrono::microseconds;
using namespace android;
@@ -38,6 +42,21 @@
static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+class DurationWrapper : public WorkDuration {
+public:
+ DurationWrapper(int64_t dur, int64_t time) {
+ durationNanos = dur;
+ timeStampNanos = time;
+ }
+};
+
+static const std::vector<WorkDuration> DURATIONS = {
+ DurationWrapper(1L, 1L),
+ DurationWrapper(1000L, 2L),
+ DurationWrapper(1000000L, 3L),
+ DurationWrapper(1000000000L, 4L),
+};
+
// Delay between oneway method calls to avoid overflowing the binder buffers.
static constexpr microseconds ONEWAY_API_DELAY = 100us;
@@ -68,6 +87,47 @@
}
}
+template <class R, class... Args0, class... Args1>
+static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...),
+ Args1&&... args1) {
+ sp<IPower> pwHal = waitForVintfService<IPower>();
+
+ if (pwHal == nullptr) {
+ ALOGI("Power HAL not available, skipping test...");
+ return;
+ }
+
+ // do not use tid from the benchmark process, use 1 for init
+ std::vector<int32_t> threadIds{1};
+ int64_t durationNanos = 16666666L;
+ sp<IPowerHintSession> hal;
+
+ auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal);
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL doesn't support session, skipping test...");
+ return;
+ }
+
+ binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ALOGI("Power HAL does not support this operation, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (ONEWAY_API_DELAY > 0us) {
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
+ .count());
+ }
+ state.ResumeTiming();
+ }
+ hal->close();
+}
+
static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
bool isSupported;
Boost boost = static_cast<Boost>(state.range(0));
@@ -90,7 +150,53 @@
runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
}
+static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) {
+ std::vector<int32_t> threadIds{static_cast<int32_t>(state.range(0))};
+ int64_t durationNanos = 16666666L;
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ sp<IPowerHintSession> appSession;
+ sp<IPower> hal = waitForVintfService<IPower>();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL not available, skipping test...");
+ return;
+ }
+
+ binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ALOGI("Power HAL does not support this operation, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ appSession->close();
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalAidlBenchmarks_getHintSessionPreferredRate(benchmark::State& state) {
+ int64_t rate;
+ runBenchmark(state, 0us, &IPower::getHintSessionPreferredRate, &rate);
+}
+
+static void BM_PowerHalAidlBenchmarks_updateTargetWorkDuration(benchmark::State& state) {
+ int64_t duration = 1000;
+ runSessionBenchmark(state, &IPowerHintSession::updateTargetWorkDuration, duration);
+}
+
+static void BM_PowerHalAidlBenchmarks_reportActualWorkDuration(benchmark::State& state) {
+ runSessionBenchmark(state, &IPowerHintSession::reportActualWorkDuration, DURATIONS);
+}
+
BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported)->DenseRange(FIRST_MODE, LAST_MODE, 1);
BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
BENCHMARK(BM_PowerHalAidlBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_createHintSession)->Arg(1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_getHintSessionPreferredRate);
+BENCHMARK(BM_PowerHalAidlBenchmarks_updateTargetWorkDuration);
+BENCHMARK(BM_PowerHalAidlBenchmarks_reportActualWorkDuration);
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index 598080b..f8abc7a 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -40,29 +40,29 @@
// Delay between oneway method calls to avoid overflowing the binder buffers.
static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
-template <class... Args0, class... Args1>
-static void runBenchmark(benchmark::State& state, HalResult (PowerHalController::*fn)(Args0...),
+template <typename T, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, HalResult<T> (PowerHalController::*fn)(Args0...),
Args1&&... args1) {
while (state.KeepRunning()) {
PowerHalController controller;
- HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
state.PauseTiming();
- if (ret == HalResult::FAILED) state.SkipWithError("Power HAL request failed");
+ if (ret.isFailed()) state.SkipWithError("Power HAL request failed");
state.ResumeTiming();
}
}
-template <class... Args0, class... Args1>
+template <typename T, class... Args0, class... Args1>
static void runCachedBenchmark(benchmark::State& state,
- HalResult (PowerHalController::*fn)(Args0...), Args1&&... args1) {
+ HalResult<T> (PowerHalController::*fn)(Args0...), Args1&&... args1) {
PowerHalController controller;
// First call out of test, to cache HAL service and isSupported result.
(controller.*fn)(std::forward<Args1>(args1)...);
while (state.KeepRunning()) {
- HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
state.PauseTiming();
- if (ret == HalResult::FAILED) {
+ if (ret.isFailed()) {
state.SkipWithError("Power HAL request failed");
}
testDelaySpin(
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 69e4041..659b2d2 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -46,7 +46,7 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V1-cpp",
+ "android.hardware.power-V2-cpp",
],
static_libs: [
"libgmock",
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
index 141b244..6cc7a6f 100644
--- a/services/powermanager/tests/PowerHalControllerTest.cpp
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -134,9 +134,9 @@
// Still works with EmptyPowerHalWrapper as fallback ignoring every api call
// and logging.
auto result = halController.setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
result = halController.setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
// PowerHalConnector was called every time to attempt to reconnect with
// underlying service.
@@ -159,9 +159,9 @@
}
auto result = mHalController->setBoost(Boost::INTERACTION, 100);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mHalController->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
// PowerHalConnector was called only once and never reset.
powerHalConnectCount = mHalConnector->getConnectCount();
@@ -182,13 +182,13 @@
EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mHalController->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
result = mHalController->setMode(Mode::VR, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mHalController->setMode(Mode::LOW_POWER, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
// PowerHalConnector was called only twice: on first api call and after failed
// call.
@@ -204,9 +204,9 @@
EXPECT_EQ(powerHalConnectCount, 0);
auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
// PowerHalConnector was called only once and never reset.
powerHalConnectCount = mHalConnector->getConnectCount();
@@ -225,7 +225,7 @@
for (int i = 0; i < 10; i++) {
threads.push_back(std::thread([&]() {
auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
@@ -253,19 +253,19 @@
for (int i = 0; i < 10; i++) {
threads.push_back(std::thread([&]() {
auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}));
threads.push_back(std::thread([&]() {
auto result = mHalController->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}));
threads.push_back(std::thread([&]() {
auto result = mHalController->setMode(Mode::LOW_POWER, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}));
threads.push_back(std::thread([&]() {
auto result = mHalController->setMode(Mode::VR, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index a765659..d890f5c 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "PowerHalWrapperAidlTest"
#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
@@ -24,11 +25,13 @@
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
+#include <unistd.h>
#include <thread>
using android::binder::Status;
using android::hardware::power::Boost;
using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
using namespace android;
@@ -44,6 +47,11 @@
MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(Status, createHintSession,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, sp<IPowerHintSession>* session),
+ (override));
+ MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
MOCK_METHOD(std::string, getInterfaceHash, (), (override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
@@ -65,7 +73,7 @@
void PowerHalWrapperAidlTest::SetUp() {
mMockHal = new StrictMock<MockIPower>();
mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
- ASSERT_NE(mWrapper, nullptr);
+ ASSERT_NE(nullptr, mWrapper);
}
// -------------------------------------------------------------------------------------------------
@@ -81,7 +89,7 @@
}
auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}
TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
@@ -99,9 +107,9 @@
}
auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
@@ -110,9 +118,9 @@
.WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
@@ -128,7 +136,7 @@
for (int i = 0; i < 10; i++) {
threads.push_back(std::thread([&]() {
auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
@@ -145,7 +153,7 @@
}
auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}
TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
@@ -163,9 +171,9 @@
}
auto result = mWrapper->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
@@ -174,9 +182,9 @@
.WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
auto result = mWrapper->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
@@ -192,8 +200,41 @@
for (int i = 0; i < 10; i++) {
threads.push_back(std::thread([&]() {
auto result = mWrapper->setMode(Mode::LAUNCH, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}));
}
std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionSuccessful) {
+ std::vector<int> threadIds{gettid()};
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ int64_t durationNanos = 16666666L;
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
+ .Times(Exactly(1));
+ auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) {
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ std::vector<int> threadIds{};
+ int64_t durationNanos = 16666666L;
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)));
+ auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) {
+ EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1));
+ auto result = mWrapper->getHintSessionPreferredRate();
+ ASSERT_TRUE(result.isOk());
+ int64_t rate = result.value();
+ ASSERT_GE(0, rate);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index 6693d0b..b54762c 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -72,7 +72,7 @@
EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
@@ -83,12 +83,12 @@
});
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
@@ -106,17 +106,17 @@
}
auto result = mWrapper->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::LOW_POWER, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::VR, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::INTERACTIVE, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
@@ -127,10 +127,10 @@
});
auto result = mWrapper->setMode(Mode::LAUNCH, 1);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index 55bbd6d..d30e8d2 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -89,7 +89,7 @@
.Times(Exactly(1));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
@@ -100,12 +100,12 @@
});
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
@@ -127,17 +127,17 @@
}
auto result = mWrapper->setMode(Mode::LAUNCH, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::LOW_POWER, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::VR, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::INTERACTIVE, true);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
- ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ ASSERT_TRUE(result.isOk());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
@@ -148,10 +148,10 @@
});
auto result = mWrapper->setMode(Mode::LAUNCH, 1);
- ASSERT_EQ(HalResult::FAILED, result);
+ ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
- ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index ca9ff7c..9aecaff 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,9 +7,6 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-subdirs = [
- "hidl"
-]
cc_library_shared {
name: "libsensorservice",
@@ -52,6 +49,8 @@
"libhardware_legacy",
"libutils",
"liblog",
+ "libactivitymanager_aidl",
+ "libbatterystats_aidl",
"libbinder",
"libsensor",
"libsensorprivacy",
@@ -71,8 +70,11 @@
generated_headers: ["framework-cppstream-protos"],
- // our public headers depend on libsensor and libsensorprivacy
- export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
+ export_shared_lib_headers: [
+ "libactivitymanager_aidl",
+ "libsensor",
+ "libsensorprivacy",
+ ],
}
cc_binary {
diff --git a/services/sensorservice/BatteryService.h b/services/sensorservice/BatteryService.h
index 43a750c..09eb2c1 100644
--- a/services/sensorservice/BatteryService.h
+++ b/services/sensorservice/BatteryService.h
@@ -17,7 +17,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
#include <utils/Singleton.h>
namespace android {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 470059a..9885352 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -60,7 +60,6 @@
"libnativewindow",
"libprocessgroup",
"libprotobuf-cpp-lite",
- "libstatslog",
"libsync",
"libtimestats",
"libui",
@@ -157,6 +156,7 @@
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
+ "HdrLayerInfoReporter.cpp",
"Layer.cpp",
"LayerProtoHelper.cpp",
"LayerRejecter.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index b3f9792..be9bce0 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -185,10 +185,14 @@
return std::nullopt;
}
}
- bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+ const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
(isSecure() && !targetSettings.isSecure);
+ const bool bufferCanBeUsedAsHwTexture =
+ mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
compositionengine::LayerFE::LayerSettings& layer = *result;
- if (blackOutLayer) {
+ if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+ ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+ mName.c_str());
prepareClearClientComposition(layer, true /* blackout */);
return layer;
}
@@ -325,6 +329,37 @@
mRefreshPending = false;
return hasReadyFrame();
}
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+ using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+ using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+ const auto frameRateCompatibility = [frameRate] {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return FrameRateCompatibility::Default;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return FrameRateCompatibility::ExactOrMultiple;
+ default:
+ return FrameRateCompatibility::Undefined;
+ }
+ }();
+
+ const auto seamlessness = [frameRate] {
+ switch (frameRate.seamlessness) {
+ case scheduler::Seamlessness::OnlySeamless:
+ return Seamlessness::ShouldBeSeamless;
+ case scheduler::Seamlessness::SeamedAndSeamless:
+ return Seamlessness::NotRequired;
+ default:
+ return Seamlessness::Undefined;
+ }
+ }();
+
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ .frameRateCompatibility = frameRateCompatibility,
+ .seamlessness = seamlessness};
+}
+} // namespace
bool BufferLayer::onPostComposition(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
@@ -355,6 +390,13 @@
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
clientCompositionTimestamp,
FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+ // Update the SurfaceFrames in the drawing state
+ if (mDrawingState.bufferSurfaceFrameTX) {
+ mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+ }
+ for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+ surfaceFrame->setGpuComposition();
+ }
}
std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
@@ -370,7 +412,9 @@
const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
- refreshRate, renderRate);
+ refreshRate, renderRate,
+ frameRateToSetFrameRateVotePayload(
+ mDrawingState.frameRate));
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
@@ -382,7 +426,9 @@
// timestamp instead.
const nsecs_t actualPresentTime = display->getRefreshTimestamp();
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
- refreshRate, renderRate);
+ refreshRate, renderRate,
+ frameRateToSetFrameRateVotePayload(
+ mDrawingState.frameRate));
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 4cd9400..ed826a0 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -278,9 +278,8 @@
return stateUpdateAvailable;
}
-// Crop that applies to the window
-Rect BufferStateLayer::getCrop(const Layer::State& /*s*/) const {
- return Rect::INVALID_RECT;
+Rect BufferStateLayer::getCrop(const Layer::State& s) const {
+ return s.crop;
}
bool BufferStateLayer::setTransform(uint32_t transform) {
@@ -301,57 +300,53 @@
}
bool BufferStateLayer::setCrop(const Rect& crop) {
- Rect c = crop;
- if (c.left < 0) {
- c.left = 0;
- }
- if (c.top < 0) {
- c.top = 0;
- }
- // If the width and/or height are < 0, make it [0, 0, -1, -1] so the equality comparision below
- // treats all invalid rectangles the same.
- if (!c.isValid()) {
- c.makeInvalid();
- }
+ if (mCurrentState.crop == crop) return false;
+ mCurrentState.sequence++;
+ mCurrentState.crop = crop;
- if (mCurrentState.crop == c) return false;
- mCurrentState.crop = c;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool BufferStateLayer::setFrame(const Rect& frame) {
- int x = frame.left;
- int y = frame.top;
- int w = frame.getWidth();
- int h = frame.getHeight();
-
- if (x < 0) {
- x = 0;
- w = frame.right;
- }
-
- if (y < 0) {
- y = 0;
- h = frame.bottom;
- }
-
- if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
- mCurrentState.width == w && mCurrentState.height == h) {
+bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
+ bool allowNonRectPreservingTransforms) {
+ if (mCurrentState.transform.dsdx() == matrix.dsdx &&
+ mCurrentState.transform.dtdy() == matrix.dtdy &&
+ mCurrentState.transform.dtdx() == matrix.dtdx &&
+ mCurrentState.transform.dsdy() == matrix.dsdy) {
return false;
}
- if (!frame.isValid()) {
- x = y = w = h = 0;
+ ui::Transform t;
+ t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+
+ if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
+ ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor "
+ "ROTATE_SURFACE_FLINGER ignored");
+ return false;
}
- mCurrentState.transform.set(x, y);
- mCurrentState.width = w;
- mCurrentState.height = h;
+
+ mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+
+ return true;
+}
+
+bool BufferStateLayer::setPosition(float x, float y) {
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+ return false;
+ }
+
+ mCurrentState.transform.set(x, y);
+
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
return true;
}
@@ -428,14 +423,20 @@
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
FrameTracer::FrameEvent::QUEUE);
}
+
+ mCurrentState.width = mCurrentState.buffer->width;
+ mCurrentState.height = mCurrentState.buffer->height;
+
return true;
}
bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
- // The acquire fences of BufferStateLayers have already signaled before they are set
- mCallbackHandleAcquireTime = fence->getSignalTime();
-
mCurrentState.acquireFence = fence;
+ mCurrentState.acquireFenceTime = std::make_unique<FenceTime>(fence);
+
+ // The acquire fences of BufferStateLayers have already signaled before they are set
+ mCallbackHandleAcquireTime = mCurrentState.acquireFenceTime->getSignalTime();
+
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -691,8 +692,8 @@
// bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
// are processing the next state.
addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
- mDrawingState.acquireFence->getSignalTime(), latchTime);
- bufferSurfaceFrame.reset();
+ mDrawingState.acquireFenceTime->getSignalTime(),
+ latchTime);
}
mCurrentStateModified = false;
@@ -856,33 +857,6 @@
return layer;
}
-Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const {
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerState();
- if (parentState.radius > 0) {
- ui::Transform t = getActiveTransform(getDrawingState());
- t = t.inverse();
- parentState.cropRect = t.transform(parentState.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
- return parentState;
- }
- }
- const float radius = getDrawingState().cornerRadius;
- const State& s(getDrawingState());
- if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
- return RoundedCornerState();
- return RoundedCornerState(FloatRect(static_cast<float>(s.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);
-}
-
bool BufferStateLayer::bufferNeedsFiltering() const {
const State& s(getDrawingState());
if (!s.buffer) {
@@ -929,6 +903,36 @@
}
}
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because BufferStateLayer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform BufferStateLayer::getInputTransform() const {
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent == nullptr) {
+ return ui::Transform();
+ }
+
+ return parent->getTransform();
+}
+
+/**
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds for BSL doesn't include buffer transform, where the input assumes
+ * that's already included.
+ */
+Rect BufferStateLayer::getInputBounds() const {
+ Rect bufferBounds = getCroppedBufferSize(getDrawingState());
+ if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
+ return bufferBounds;
+ }
+ return mDrawingState.transform.transform(bufferBounds);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 7a3da6f..8ce3e1f 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -66,7 +66,6 @@
bool setTransform(uint32_t transform) override;
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
- bool setFrame(const Rect& frame) override;
bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
nsecs_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& clientCacheId, uint64_t frameNumber,
@@ -81,15 +80,13 @@
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime) override;
+ bool setPosition(float /*x*/, float /*y*/) override;
+ bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
+ bool /*allowNonRectPreservingTransforms*/);
// Override to ignore legacy layer state properties that are not used by BufferStateLayer
bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
- bool setPosition(float /*x*/, float /*y*/) override { return false; }
bool setTransparentRegionHint(const Region& transparent) override;
- bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
- bool /*allowNonRectPreservingTransforms*/) override {
- return false;
- }
void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
uint64_t /*frameNumber*/) override {}
void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -97,7 +94,6 @@
Rect getBufferSize(const State& s) const override;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
- Layer::RoundedCornerState getRoundedCornerState() const override;
void setAutoRefresh(bool autoRefresh) override;
// -----------------------------------------------------------------------
@@ -122,6 +118,8 @@
void gatherBufferInfo() override;
uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
+ ui::Transform getInputTransform() const override;
+ Rect getInputBounds() const override;
private:
friend class SlotGenerationTest;
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2ae49fa..44b33ef 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -48,7 +48,7 @@
auto bufItr = processBuffers.find(id);
if (bufItr == processBuffers.end()) {
- ALOGE("failed to get buffer, invalid buffer id");
+ ALOGV("failed to get buffer, invalid buffer id");
return false;
}
@@ -150,7 +150,7 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
- ALOGE("failed to register erased recipient, could not retrieve buffer");
+ ALOGV("failed to register erased recipient, could not retrieve buffer");
return false;
}
buf->recipients.insert(recipient);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8402149..a45be8a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -55,6 +55,16 @@
std::vector<uint8_t> value;
std::string dumpAsString() const;
+
+ struct Hasher {
+ size_t operator()(const GenericLayerMetadataEntry& entry) const {
+ size_t hash = 0;
+ for (const auto value : entry.value) {
+ hashCombineSingleHashed(hash, value);
+ }
+ return hash;
+ }
+ };
};
inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
@@ -70,6 +80,8 @@
/*
* Used by LayerFE::getCompositionState
+ * Note that fields that affect HW composer state may need to be mirrored into
+ * android::compositionengine::impl::planner::LayerState
*/
struct LayerFECompositionState {
// If set to true, forces client composition on all output layers until
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index f113c34..ae88e78 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -55,7 +55,6 @@
std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
void dump(std::string&) const override;
-
virtual FloatRect calculateOutputSourceCrop() const;
virtual Rect calculateOutputDisplayFrame() const;
virtual uint32_t calculateOutputRelativeBufferTransform(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index a3e84e2..48a54d6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -16,10 +16,7 @@
#pragma once
-#include <cstdint>
-#include <optional>
-#include <string>
-
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/HwcBufferCache.h>
#include <renderengine/Mesh.h>
#include <ui/FloatRect.h>
@@ -27,6 +24,10 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <cstdint>
+#include <optional>
+#include <string>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -47,6 +48,8 @@
namespace compositionengine::impl {
+// Note that fields that affect HW composer state may need to be mirrored into
+// android::compositionengine::impl::planner::LayerState
struct OutputLayerCompositionState {
// The portion of the layer that is not obscured by opaque layers on top
Region visibleRegion;
@@ -90,6 +93,9 @@
sp<Fence> acquireFence = nullptr;
Rect displayFrame = {};
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+ ProjectionSpace displaySpace;
+ Region damageRegion = Region::INVALID_REGION;
+ Region visibleRegion;
} overrideInfo;
/*
@@ -109,6 +115,9 @@
// The buffer cache for this layer. This is used to lower the
// cost of sending reused buffers to the HWC.
HwcBufferCache hwcBufferCache;
+
+ // Set to true when overridden info has been sent to HW composer
+ bool stateOverridden = false;
};
// The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index c9395ca..c5d03a7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -16,6 +16,8 @@
#pragma once
+#include <compositionengine/Output.h>
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <renderengine/RenderEngine.h>
@@ -38,7 +40,10 @@
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(); }
+ const Region& getVisibleRegion() const { return mState->getVisibleRegion(); }
+ const sp<GraphicBuffer>& getBuffer() const {
+ return mState->getOutputLayer()->getLayerFE().getCompositionState()->buffer;
+ }
int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
NonBufferHash getHash() const { return mHash; }
std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
@@ -59,9 +64,11 @@
size_t getLayerCount() const { return mLayers.size(); }
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
+ const Region& getVisibleRegion() const { return mVisibleRegion; }
size_t getAge() const { return mAge; }
const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
const sp<Fence>& getDrawFence() const { return mDrawFence; }
+ const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
NonBufferHash getNonBufferHash() const;
@@ -70,7 +77,7 @@
size_t getCreationCost() const;
size_t getDisplayCost() const;
- bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const;
+ bool hasBufferUpdate() const;
bool hasReadyBuffer() const;
// Decomposes this CachedSet into a vector of its layers as individual CachedSets
@@ -89,11 +96,12 @@
boundingRegion.orSelf(mBounds);
boundingRegion.orSelf(other.mBounds);
mBounds = boundingRegion.getBounds();
+ mVisibleRegion.orSelf(other.mVisibleRegion);
}
void incrementAge() { ++mAge; }
- // Renders the cached set with the supplied output dataspace.
- void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+ // Renders the cached set with the supplied output composition state.
+ void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
void dump(std::string& result) const;
@@ -104,6 +112,7 @@
std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
std::vector<Layer> mLayers;
Rect mBounds = Rect::EMPTY_RECT;
+ Region mVisibleRegion;
size_t mAge = 0;
class Texture {
@@ -132,7 +141,9 @@
Texture mTexture;
sp<Fence> mDrawFence;
+ ProjectionSpace mOutputSpace;
ui::Dataspace mOutputDataspace;
+ ui::Transform::RotationFlags mOrientation = ui::Transform::ROT_0;
static const bool sDebugHighlighLayers;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 7a6ceab..2f2ad4c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -16,6 +16,7 @@
#pragma once
+#include <compositionengine/Output.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>
@@ -42,10 +43,9 @@
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();
+ // Renders the newest cached sets with the supplied output composition state
+ void renderCachedSets(renderengine::RenderEngine& re,
+ const OutputCompositionState& outputState);
void dump(std::string& result) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index a3beadc..050e264 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -32,6 +32,14 @@
struct hash<android::sp<T>> {
size_t operator()(const android::sp<T>& p) { return std::hash<void*>()(p.get()); }
};
+
+template <typename T>
+struct hash<android::wp<T>> {
+ size_t operator()(const android::wp<T>& p) {
+ android::sp<T> promoted = p.promote();
+ return std::hash<void*>()(promoted ? promoted.get() : nullptr);
+ }
+};
} // namespace std
namespace android::compositionengine::impl::planner {
@@ -49,13 +57,16 @@
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,
+ LayerMetadata = 1u << 8,
+ VisibleRegion = 1u << 9,
+ Dataspace = 1u << 10,
+ PixelFormat = 1u << 11,
+ ColorTransform = 1u << 12,
+ SurfaceDamage = 1u << 13,
+ CompositionType = 1u << 14,
+ SidebandStream = 1u << 15,
+ Buffer = 1u << 16,
+ SolidColor = 1u << 17,
};
// clang-format on
@@ -70,7 +81,7 @@
virtual Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) = 0;
- virtual size_t getHash(Flags<LayerStateField> skipFields) const = 0;
+ virtual size_t getHash() const = 0;
virtual LayerStateField getField() const = 0;
@@ -87,6 +98,7 @@
using ReadFromLayerState = std::function<T(const compositionengine::OutputLayer* layer)>;
using ToStrings = std::function<std::vector<std::string>(const T&)>;
using Equals = std::function<bool(const T&, const T&)>;
+ using Hashes = std::function<size_t(const T&)>;
static ToStrings getDefaultToStrings() {
return [](const T& value) {
@@ -99,20 +111,48 @@
return [](const T& value) { return std::vector<std::string>{toString(value)}; };
}
+ static ToStrings getRegionToStrings() {
+ return [](const Region& region) {
+ using namespace std::string_literals;
+ std::string dump;
+ region.dump(dump, "");
+ std::vector<std::string> split = base::Split(dump, "\n"s);
+ split.erase(split.begin()); // Strip the header
+ split.pop_back(); // Strip the last (empty) line
+ for (std::string& line : split) {
+ line.erase(0, 4); // Strip leading padding before each rect
+ }
+ return split;
+ };
+ }
+
static Equals getDefaultEquals() {
return [](const T& lhs, const T& rhs) { return lhs == rhs; };
}
+ static Equals getRegionEquals() {
+ return [](const Region& lhs, const Region& rhs) { return lhs.hasSameRects(rhs); };
+ }
+
+ static Hashes getDefaultHashes() {
+ return [](const T& value) { return std::hash<T>{}(value); };
+ }
+
OutputLayerState(ReadFromLayerState reader,
ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
- Equals equals = OutputLayerState::getDefaultEquals())
- : mReader(reader), mToStrings(toStrings), mEquals(equals) {}
+ Equals equals = OutputLayerState::getDefaultEquals(),
+ Hashes hashes = OutputLayerState::getDefaultHashes())
+ : mReader(reader), mToStrings(toStrings), mEquals(equals), mHashes(hashes) {}
~OutputLayerState() override = default;
// Returns this member's field flag if it was changed
Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) override {
T newValue = mReader(layer);
+ return update(newValue);
+ }
+
+ Flags<LayerStateField> update(const T& newValue) {
if (!mEquals(mValue, newValue)) {
mValue = newValue;
mHash = {};
@@ -124,12 +164,9 @@
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;
- }
+ size_t getHash() const override {
if (!mHash) {
- mHash = std::hash<T>{}(mValue);
+ mHash = mHashes(mValue);
}
return *mHash;
}
@@ -163,6 +200,7 @@
const ReadFromLayerState mReader;
const ToStrings mToStrings;
const Equals mEquals;
+ const Hashes mHashes;
T mValue = {};
mutable std::optional<size_t> mHash = {};
};
@@ -175,22 +213,22 @@
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;
+ // The hash is only computed from NonUniqueFields, and excludes GraphicBuffers since they are
+ // not guaranteed to live longer than the LayerState object.
+ size_t getHash() const;
// Returns the bit-set of differing fields between this LayerState and another LayerState.
- // This bit-set is based on NonUniqueFields only
- Flags<LayerStateField> getDifferingFields(const LayerState& other,
- Flags<LayerStateField> skipFields) const;
+ // This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers.
+ Flags<LayerStateField> getDifferingFields(const LayerState& other) const;
compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
int32_t getId() const { return mId.get(); }
const std::string& getName() const { return mName.get(); }
Rect getDisplayFrame() const { return mDisplayFrame.get(); }
+ const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
hardware::graphics::composer::hal::Composition getCompositionType() const {
return mCompositionType.get();
}
- const sp<GraphicBuffer>& getBuffer() const { return mBuffer.get(); }
void incrementFramesSinceBufferUpdate() { ++mFramesSinceBufferUpdate; }
void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
@@ -255,39 +293,80 @@
OutputLayerState<float, LayerStateField::Alpha> mAlpha{
[](auto layer) { return layer->getLayerFE().getCompositionState()->alpha; }};
- // TODO(b/180638831): Generic layer metadata
+ using LayerMetadataState =
+ OutputLayerState<GenericLayerMetadataMap, LayerStateField::LayerMetadata>;
+ LayerMetadataState
+ mLayerMetadata{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->metadata;
+ },
+ [](const GenericLayerMetadataMap& metadata) {
+ std::vector<std::string> result;
+ if (metadata.empty()) {
+ result.push_back("{}");
+ return result;
+ }
+ result.push_back("{");
+ for (const auto& [key, value] : metadata) {
+ std::string keyValueDump;
+ keyValueDump.append(" ");
+ keyValueDump.append(key);
+ keyValueDump.append("=");
+ keyValueDump.append(value.dumpAsString());
+ result.push_back(keyValueDump);
+ }
+ result.push_back("}");
+ return result;
+ },
+ LayerMetadataState::getDefaultEquals(),
+ [](const GenericLayerMetadataMap& metadata) {
+ size_t hash = 0;
+ for (const auto& [key, value] : metadata) {
+ size_t entryHash = 0;
+ hashCombineSingleHashed(entryHash,
+ std::hash<std::string>{}(key));
+ hashCombineSingleHashed(entryHash,
+ GenericLayerMetadataEntry::Hasher{}(
+ value));
+ hash ^= entryHash;
+ }
+ return hash;
+ }};
// Output-dependent per-frame state
- 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 VisibleRegionState = OutputLayerState<Region, LayerStateField::VisibleRegion>;
+ VisibleRegionState mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+ VisibleRegionState::getRegionToStrings(),
+ VisibleRegionState::getRegionEquals()};
using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
DataspaceState::getHalToStrings()};
- // TODO(b/180638831): Buffer format
-
// Output-independent per-frame state
+ using PixelFormatState = OutputLayerState<hardware::graphics::composer::hal::PixelFormat,
+ LayerStateField::PixelFormat>;
+ PixelFormatState
+ mPixelFormat{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->buffer
+ ? static_cast<hardware::graphics::composer::hal::PixelFormat>(
+ layer->getLayerFE()
+ .getCompositionState()
+ ->buffer->getPixelFormat())
+ : hardware::graphics::composer::hal::PixelFormat::RGBA_8888;
+ },
+ PixelFormatState::getHalToStrings()};
+
OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
- // TODO(b/180638831): Surface damage
+ using SurfaceDamageState = OutputLayerState<Region, LayerStateField::SurfaceDamage>;
+ SurfaceDamageState
+ mSurfaceDamage{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->surfaceDamage;
+ },
+ SurfaceDamageState::getRegionToStrings(),
+ SurfaceDamageState::getRegionEquals()};
using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
LayerStateField::CompositionType>;
@@ -311,10 +390,14 @@
return std::vector<std::string>{base::StringPrintf("%p", p)};
}};
- OutputLayerState<sp<GraphicBuffer>, LayerStateField::Buffer>
+ OutputLayerState<wp<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())};
+ [](const wp<GraphicBuffer>& buffer) {
+ sp<GraphicBuffer> promotedBuffer = buffer.promote();
+ return std::vector<std::string>{
+ base::StringPrintf("%p",
+ promotedBuffer ? promotedBuffer.get()
+ : nullptr)};
}};
int64_t mFramesSinceBufferUpdate = 0;
@@ -327,10 +410,12 @@
return std::vector<std::string>{stream.str()};
}};
- std::array<StateInterface*, 13> getNonUniqueFields() {
- std::array<const StateInterface*, 13> constFields =
+ static const constexpr size_t kNumNonUniqueFields = 16;
+
+ std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
+ std::array<const StateInterface*, kNumNonUniqueFields> constFields =
const_cast<const LayerState*>(this)->getNonUniqueFields();
- std::array<StateInterface*, 13> fields;
+ std::array<StateInterface*, kNumNonUniqueFields> fields;
std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
[](const StateInterface* constField) {
return const_cast<StateInterface*>(constField);
@@ -338,12 +423,12 @@
return fields;
}
- std::array<const StateInterface*, 13> getNonUniqueFields() const {
+ std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
return {
- &mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
- &mBlendMode, &mAlpha, &mVisibleRegion, &mOutputDataspace,
- &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
- &mSolidColor,
+ &mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
+ &mBlendMode, &mAlpha, &mLayerMetadata, &mVisibleRegion,
+ &mOutputDataspace, &mPixelFormat, &mColorTransform, &mSurfaceDamage,
+ &mCompositionType, &mSidebandStream, &mBuffer, &mSolidColor,
};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index 89de34d..e6d2b63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,8 @@
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 renderCachedSets(renderengine::RenderEngine& re,
+ const OutputCompositionState& outputState);
void dump(const Vector<String16>& args, std::string&);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ded2dcc..3ac5433 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -722,10 +722,7 @@
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;
+ const bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
layer->writeStateToHWC(includeGeometry, skipLayer);
}
}
@@ -1104,6 +1101,7 @@
Region stubRegion;
bool disableBlurs = false;
+ sp<GraphicBuffer> previousOverrideBuffer = nullptr;
for (auto* layer : getOutputLayersOrderedByZ()) {
const auto& layerState = layer->getState();
@@ -1153,8 +1151,14 @@
std::vector<LayerFE::LayerSettings> results;
if (layer->getState().overrideInfo.buffer != nullptr) {
- results = layer->getOverrideCompositionList();
- ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ if (layer->getState().overrideInfo.buffer != previousOverrideBuffer) {
+ results = layer->getOverrideCompositionList();
+ previousOverrideBuffer = layer->getState().overrideInfo.buffer;
+ ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ } else {
+ ALOGV("Skipping redundant override buffer for [%s] in RE",
+ layer->getLayerFE().getDebugName());
+ }
} else {
results = layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
@@ -1252,7 +1256,7 @@
void Output::renderCachedSets() {
if (mPlanner) {
- mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b364649..f640f85 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -333,7 +333,11 @@
auto requestedCompositionType = outputIndependentState->compositionType;
- if (includeGeometry) {
+ // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+ // only when the geometry actually changes
+ const bool isOverridden = state.overrideInfo.buffer != nullptr;
+ const bool prevOverridden = state.hwc->stateOverridden;
+ if (isOverridden || prevOverridden || skipLayer || includeGeometry) {
writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
skipLayer);
@@ -346,6 +350,8 @@
// Always set the layer color after setting the composition type.
writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+
+ editState().hwc->stateOverridden = isOverridden;
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(
@@ -354,7 +360,8 @@
Rect displayFrame = outputDependentState.displayFrame;
FloatRect sourceCrop = outputDependentState.sourceCrop;
- if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
+
+ if (outputDependentState.overrideInfo.buffer != nullptr) {
displayFrame = outputDependentState.overrideInfo.displayFrame;
sourceCrop = displayFrame.toFloatRect();
}
@@ -380,8 +387,9 @@
outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
}
- // Solid-color layers should always use an identity transform.
- const auto bufferTransform = requestedCompositionType != hal::Composition::SOLID_COLOR
+ // Solid-color layers and overridden buffers should always use an identity transform.
+ const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+ getState().overrideInfo.buffer == nullptr)
? outputDependentState.bufferTransform
: static_cast<hal::Transform>(0);
if (auto error = hwcLayer->setTransform(static_cast<hal::Transform>(bufferTransform));
@@ -395,14 +403,17 @@
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
- if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
- error != hal::Error::NONE) {
+ const auto blendMode = getState().overrideInfo.buffer
+ ? hardware::graphics::composer::hal::BlendMode::PREMULTIPLIED
+ : outputIndependentState.blendMode;
+ if (auto error = hwcLayer->setBlendMode(blendMode); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
- toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
+ toString(blendMode).c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
}
- const float alpha = skipLayer ? 0.0f : outputIndependentState.alpha;
+ const float alpha = skipLayer
+ ? 0.0f
+ : (getState().overrideInfo.buffer ? 1.0f : outputIndependentState.alpha);
ALOGV("Writing alpha %f", alpha);
if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
@@ -424,18 +435,22 @@
// TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry
// state and should not change every frame.
- if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
- error != hal::Error::NONE) {
+ Region visibleRegion = outputDependentState.overrideInfo.buffer
+ ? Region(outputDependentState.overrideInfo.visibleRegion)
+ : outputDependentState.outputSpaceVisibleRegion;
+ if (auto error = hwcLayer->setVisibleRegion(visibleRegion); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
}
- if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
- outputDependentState.dataspace, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ const auto dataspace = outputDependentState.overrideInfo.buffer
+ ? outputDependentState.overrideInfo.dataspace
+ : outputDependentState.dataspace;
+
+ if (auto error = hwcLayer->setDataspace(dataspace); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -452,8 +467,11 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
- error != hal::Error::NONE) {
+ const Region& surfaceDamage = getState().overrideInfo.buffer
+ ? getState().overrideInfo.damageRegion
+ : outputIndependentState.surfaceDamage;
+
+ if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
outputIndependentState.surfaceDamage.dump(LOG_TAG);
@@ -671,16 +689,26 @@
return {};
}
+ // Compute the geometry boundaries in layer stack space: we need to transform from the
+ // framebuffer space of the override buffer to layer space.
+ const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace;
+ const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace);
+ const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame);
+
LayerFE::LayerSettings settings;
settings.geometry = renderengine::Geometry{
- .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+ .boundaries = boundaries.toFloatRect(),
};
settings.bufferId = getState().overrideInfo.buffer->getId();
- settings.source =
- renderengine::PixelSource{.buffer = renderengine::Buffer{
- .buffer = getState().overrideInfo.buffer,
- .fence = getState().overrideInfo.acquireFence,
- }};
+ settings.source = renderengine::PixelSource{
+ .buffer = renderengine::Buffer{
+ .buffer = getState().overrideInfo.buffer,
+ .fence = getState().overrideInfo.acquireFence,
+ // If the transform from layer space to display space contains a rotation, we
+ // need to undo the rotation in the texture transform
+ .textureTransform =
+ ui::Transform(transform.inverse().getOrientation(), 1, 1).asMatrix4(),
+ }};
settings.sourceDataspace = getState().overrideInfo.dataspace;
settings.alpha = 1.0f;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 45dce98..efd23dc 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,13 @@
dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
dumpVal(out, "override display frame", overrideInfo.displayFrame);
dumpVal(out, "override dataspace", toString(overrideInfo.dataspace), overrideInfo.dataspace);
+ dumpVal(out, "override display space", to_string(overrideInfo.displaySpace));
+ std::string damageRegionString;
+ overrideInfo.damageRegion.dump(damageRegionString, "");
+ dumpVal(out, "override damage region", damageRegionString);
+ std::string visibleRegionString;
+ overrideInfo.visibleRegion.dump(visibleRegionString, "");
+ dumpVal(out, "override visible region", visibleRegionString);
if (hwc) {
dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 137697b..dcb7555 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -19,6 +19,7 @@
// #define LOG_NDEBUG 0
#include <android-base/properties.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <math/HashCombine.h>
#include <renderengine/DisplaySettings.h>
@@ -50,17 +51,18 @@
}
CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
- : mState(state), mHash(state->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+ : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
- : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+ : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
addLayer(layer, lastUpdate);
}
CachedSet::CachedSet(Layer layer)
: mFingerprint(layer.getHash()),
mLastUpdate(layer.getLastUpdate()),
- mBounds(layer.getDisplayFrame()) {
+ mBounds(layer.getDisplayFrame()),
+ mVisibleRegion(layer.getVisibleRegion()) {
mLayers.emplace_back(std::move(layer));
}
@@ -72,6 +74,7 @@
boundingRegion.orSelf(mBounds);
boundingRegion.orSelf(layer->getDisplayFrame());
mBounds = boundingRegion.getBounds();
+ mVisibleRegion.orSelf(layer->getVisibleRegion());
}
NonBufferHash CachedSet::getNonBufferHash() const {
@@ -84,6 +87,7 @@
size_t hash = 0;
android::hashCombineSingle(hash, mBounds);
android::hashCombineSingle(hash, mOutputDataspace);
+ android::hashCombineSingle(hash, mOrientation);
return hash;
}
@@ -116,12 +120,11 @@
return static_cast<size_t>(mBounds.width() * mBounds.height());
}
-bool CachedSet::hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const {
+bool CachedSet::hasBufferUpdate() const {
for (const Layer& layer : mLayers) {
if (layer.getFramesSinceBufferUpdate() == 0) {
return true;
}
- ++layers;
}
return false;
}
@@ -149,17 +152,22 @@
}
}
-void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine,
+ const OutputCompositionState& outputState) {
+ const Rect& viewport = outputState.layerStackSpace.content;
+ const ui::Dataspace& outputDataspace = outputState.dataspace;
+ const ui::Transform::RotationFlags orientation =
+ ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
renderengine::DisplaySettings displaySettings{
.physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
- .clip = mBounds,
+ .clip = viewport,
.outputDataspace = outputDataspace,
+ .orientation = orientation,
};
Region clearRegion = Region::INVALID_REGION;
- Rect viewport = mBounds;
LayerFE::ClientCompositionTargetSettings targetSettings{
- .clip = Region(mBounds),
+ .clip = Region(viewport),
.needsFiltering = false,
.isSecure = true,
.supportsProtectedContent = false,
@@ -172,6 +180,7 @@
};
std::vector<renderengine::LayerSettings> layerSettings;
+ renderengine::LayerSettings highlight;
for (const auto& layer : mLayers) {
const auto clientCompositionList =
layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
@@ -186,7 +195,7 @@
[](const renderengine::LayerSettings& settings) { return &settings; });
if (sDebugHighlighLayers) {
- renderengine::LayerSettings highlight{
+ highlight = {
.geometry =
renderengine::Geometry{
.boundaries = FloatRect(0.0f, 0.0f,
@@ -217,7 +226,12 @@
if (result == NO_ERROR) {
mTexture.setBuffer(buffer, &renderEngine);
mDrawFence = new Fence(drawFence.release());
+ mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
+ outputState.framebufferSpace.bounds.getHeight()),
+ mBounds);
+ mOutputSpace.orientation = outputState.framebufferSpace.orientation;
mOutputDataspace = outputDataspace;
+ mOrientation = orientation;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 88193cc..9c9649c 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -56,24 +56,12 @@
}
void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
- ui::Dataspace outputDataspace) {
+ const OutputCompositionState& outputState) {
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();
+ mNewCachedSet->render(renderEngine, outputState);
}
void Flattener::dump(std::string& result) const {
@@ -212,10 +200,8 @@
auto currentLayerIter = mLayers.begin();
auto incomingLayerIter = layers.begin();
while (incomingLayerIter != layers.end()) {
- if (mNewCachedSet &&
- mNewCachedSet->getFingerprint() ==
- (*incomingLayerIter)->getHash(LayerStateField::Buffer)) {
- if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) {
+ if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
+ if (mNewCachedSet->hasBufferUpdate()) {
ALOGV("[%s] Dropping new cached set", __func__);
++mInvalidatedCachedSetAges[0];
mNewCachedSet = std::nullopt;
@@ -232,6 +218,9 @@
.acquireFence = mNewCachedSet->getDrawFence(),
.displayFrame = mNewCachedSet->getBounds(),
.dataspace = mNewCachedSet->getOutputDataspace(),
+ .displaySpace = mNewCachedSet->getOutputSpace(),
+ .damageRegion = Region::INVALID_REGION,
+ .visibleRegion = mNewCachedSet->getVisibleRegion(),
};
++incomingLayerIter;
}
@@ -249,7 +238,7 @@
}
}
- if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) {
+ if (!currentLayerIter->hasBufferUpdate()) {
currentLayerIter->incrementAge();
merged.emplace_back(*currentLayerIter);
@@ -263,6 +252,9 @@
.acquireFence = currentLayerIter->getDrawFence(),
.displayFrame = currentLayerIter->getBounds(),
.dataspace = currentLayerIter->getOutputDataspace(),
+ .displaySpace = currentLayerIter->getOutputSpace(),
+ .damageRegion = Region(),
+ .visibleRegion = currentLayerIter->getVisibleRegion(),
};
++incomingLayerIter;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 222b2be..7c32e94 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -58,24 +58,24 @@
return differences;
}
-size_t LayerState::getHash(
- Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+size_t LayerState::getHash() const {
size_t hash = 0;
for (const StateInterface* field : getNonUniqueFields()) {
- android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+ if (field->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+ android::hashCombineSingleHashed(hash, field->getHash());
}
return hash;
}
-Flags<LayerStateField> LayerState::getDifferingFields(
- const LayerState& other,
- Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const {
Flags<LayerStateField> differences;
auto myFields = getNonUniqueFields();
auto otherFields = other.getNonUniqueFields();
for (size_t i = 0; i < myFields.size(); ++i) {
- if (skipFields.test(myFields[i]->getField())) {
+ if (myFields[i]->getField() == LayerStateField::Buffer) {
continue;
}
@@ -157,9 +157,11 @@
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.mAlpha == rhs.mAlpha && lhs.mLayerMetadata == rhs.mLayerMetadata &&
+ lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mOutputDataspace == rhs.mOutputDataspace && lhs.mPixelFormat == rhs.mPixelFormat &&
lhs.mColorTransform == rhs.mColorTransform &&
+ lhs.mSurfaceDamage == rhs.mSurfaceDamage &&
lhs.mCompositionType == rhs.mCompositionType &&
lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
(lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
@@ -169,7 +171,7 @@
NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
size_t hash = 0;
for (const auto layer : layers) {
- android::hashCombineSingleHashed(hash, layer->getHash(LayerStateField::Buffer));
+ android::hashCombineSingleHashed(hash, layer->getHash());
}
return hash;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 87721c7..ad75557 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -134,8 +134,8 @@
}
void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
- ui::Dataspace outputDataspace) {
- mFlattener.renderCachedSets(renderEngine, outputDataspace);
+ const OutputCompositionState& outputState) {
+ mFlattener.renderCachedSets(renderEngine, outputState);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 07920b8..8226ef7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -33,8 +33,7 @@
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)) {
+ if (mLayers[i].getHash() == other[i]->getHash()) {
continue;
}
@@ -56,8 +55,7 @@
return std::nullopt;
}
- Flags<LayerStateField> differingFields =
- mLayers[i].getDifferingFields(*other[i], LayerStateField::Buffer);
+ Flags<LayerStateField> differingFields = mLayers[i].getDifferingFields(*other[i]);
// If we don't find an approximate match on this layer, then the LayerStacks differ
// by too much, so return nothing
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9dd199d..8a4d161 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -690,10 +690,15 @@
static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
static constexpr uint32_t kZOrder = 21u;
static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
+ static constexpr Hwc2::Transform kOverrideBufferTransform = static_cast<Hwc2::Transform>(0);
static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
static_cast<Hwc2::IComposerClient::BlendMode>(41);
+ static constexpr Hwc2::IComposerClient::BlendMode kOverrideBlendMode =
+ Hwc2::IComposerClient::BlendMode::PREMULTIPLIED;
static constexpr float kAlpha = 51.f;
+ static constexpr float kOverrideAlpha = 1.f;
static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+ static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
@@ -701,13 +706,18 @@
static const half4 kColor;
static const Rect kDisplayFrame;
+ static const Rect kOverrideDisplayFrame;
static const Region kOutputSpaceVisibleRegion;
+ static const Region kOverrideVisibleRegion;
static const mat4 kColorTransform;
static const Region kSurfaceDamage;
+ static const Region kOverrideSurfaceDamage;
static const HdrMetadata kHdrMetadata;
static native_handle_t* kSidebandStreamHandle;
static const sp<GraphicBuffer> kBuffer;
+ static const sp<GraphicBuffer> kOverrideBuffer;
static const sp<Fence> kFence;
+ static const sp<Fence> kOverrideFence;
static const std::string kLayerGenericMetadata1Key;
static const std::vector<uint8_t> kLayerGenericMetadata1Value;
static const std::string kLayerGenericMetadata2Key;
@@ -751,26 +761,42 @@
kLayerGenericMetadata2Value};
}
- void expectGeometryCommonCalls() {
- EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setTransform(kBufferTransform)).WillOnce(Return(kError));
+ void includeOverrideInfo() {
+ auto& overrideInfo = mOutputLayer.editState().overrideInfo;
- EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
+ overrideInfo.buffer = kOverrideBuffer;
+ overrideInfo.acquireFence = kOverrideFence;
+ overrideInfo.displayFrame = kOverrideDisplayFrame;
+ overrideInfo.dataspace = kOverrideDataspace;
+ overrideInfo.damageRegion = kOverrideSurfaceDamage;
+ overrideInfo.visibleRegion = kOverrideVisibleRegion;
}
- void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
- EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
- .WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+ void expectGeometryCommonCalls(Rect displayFrame = kDisplayFrame,
+ FloatRect sourceCrop = kSourceCrop,
+ Hwc2::Transform bufferTransform = kBufferTransform,
+ Hwc2::IComposerClient::BlendMode blendMode = kBlendMode,
+ float alpha = kAlpha) {
+ EXPECT_CALL(*mHwcLayer, setDisplayFrame(displayFrame)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSourceCrop(sourceCrop)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setTransform(bufferTransform)).WillOnce(Return(kError));
+
+ EXPECT_CALL(*mHwcLayer, setBlendMode(blendMode)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setPlaneAlpha(alpha)).WillOnce(Return(kError));
+ }
+
+ void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
+ ui::Dataspace dataspace = kDataspace,
+ const Region& visibleRegion = kOutputSpaceVisibleRegion,
+ const Region& surfaceDamage = kSurfaceDamage) {
+ EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
.WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
? hal::Error::UNSUPPORTED
: hal::Error::NONE));
- EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
- .WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
}
void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
@@ -793,9 +819,10 @@
EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
}
- void expectSetHdrMetadataAndBufferCalls() {
+ void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+ sp<Fence> fence = kFence) {
EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
- EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+ EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
}
void expectGenericLayerMetadataCalls() {
@@ -817,18 +844,23 @@
const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
84.f / 255.f};
const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
Rect{1005, 1006, 1007, 1008}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024,
};
const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideSurfaceDamage{Rect{1026, 1027, 1028, 1029}};
const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
reinterpret_cast<native_handle_t*>(1031);
const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer = new GraphicBuffer();
const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
"com.example.metadata.1";
const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
@@ -983,6 +1015,20 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect(),
+ kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage);
+ expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+}
+
/*
* OutputLayer::writeCursorPositionToHWC()
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 377f817..f01fe27 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
@@ -59,6 +60,7 @@
static constexpr size_t kNumLayers = 5;
std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+ impl::OutputCompositionState mOutputState;
android::renderengine::mock::RenderEngine mRenderEngine;
};
@@ -68,6 +70,8 @@
auto testLayer = std::make_unique<TestLayer>();
auto pos = static_cast<int32_t>(i);
testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+ testLayer->outputLayerCompositionState.visibleRegion =
+ Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
testLayer->layerFE = sp<mock::LayerFE>::make();
@@ -87,6 +91,12 @@
std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
mTestLayers.emplace_back(std::move(testLayer));
+
+ // set up minimium params needed for rendering
+ mOutputState.dataspace = ui::Dataspace::SRGB;
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
}
}
@@ -98,6 +108,7 @@
EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
+ EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion()));
EXPECT_EQ(1u, cachedSet.getLayerCount());
EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState());
EXPECT_EQ(0u, cachedSet.getAge());
@@ -146,6 +157,10 @@
EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
+ Region expectedRegion;
+ expectedRegion.orSelf(Rect(1, 1, 2, 2));
+ expectedRegion.orSelf(Rect(2, 2, 3, 3));
+ EXPECT_TRUE(cachedSet.getVisibleRegion().hasSameRects(expectedRegion));
EXPECT_EQ(2u, cachedSet.getLayerCount());
EXPECT_EQ(0u, cachedSet.getAge());
expectNoBuffer(cachedSet);
@@ -201,13 +216,7 @@
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()));
+ EXPECT_FALSE(cachedSet.hasBufferUpdate());
}
TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) {
@@ -221,13 +230,7 @@
mTestLayers[1]->layerState->resetFramesSinceBufferUpdate();
- std::vector<const LayerState*> incomingLayers = {
- layer1.getState(),
- layer2.getState(),
- layer3.getState(),
- };
-
- EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+ EXPECT_TRUE(cachedSet.hasBufferUpdate());
}
TEST_F(CachedSetTest, append) {
@@ -243,6 +246,11 @@
EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
+ Region expectedRegion;
+ expectedRegion.orSelf(Rect(1, 1, 2, 2));
+ expectedRegion.orSelf(Rect(2, 2, 3, 3));
+ expectedRegion.orSelf(Rect(3, 3, 4, 4));
+ EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
EXPECT_EQ(3u, cachedSet1.getLayerCount());
EXPECT_EQ(0u, cachedSet1.getAge());
expectNoBuffer(cachedSet1);
@@ -300,7 +308,9 @@
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(mOutputState.layerStackSpace.content, displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ displaySettings.orientation);
EXPECT_EQ(0.5f, layers[0]->alpha);
EXPECT_EQ(0.75f, layers[1]->alpha);
EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
@@ -312,9 +322,14 @@
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
- cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+ cachedSet.render(mRenderEngine, mOutputState);
expectReadyBuffer(cachedSet);
+ EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content);
+ EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(),
+ mOutputState.framebufferSpace.bounds.getHeight()),
+ cachedSet.getOutputSpace().bounds);
+
// Now check that appending a new cached set properly cleans up RenderEngine resources.
EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index bd77559..c528087 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
@@ -76,6 +77,7 @@
static constexpr size_t kNumLayers = 5;
std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+ impl::OutputCompositionState mOutputState;
};
void FlattenerTest::SetUp() {
@@ -87,6 +89,8 @@
testLayer->name = ss.str();
testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+ testLayer->outputLayerCompositionState.visibleRegion =
+ Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
testLayer->layerFECompositionState.buffer =
new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -120,6 +124,11 @@
testLayer->layerState->incrementFramesSinceBufferUpdate();
mTestLayers.emplace_back(std::move(testLayer));
+
+ // set up minimium params needed for rendering
+ mOutputState.dataspace = ui::Dataspace::SRGB;
+ mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
}
}
@@ -134,13 +143,13 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
// same geometry, update the internal layer stack
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
}
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -150,7 +159,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -160,7 +169,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
EXPECT_NE(nullptr, buffer);
@@ -195,7 +204,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
}
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -241,13 +250,87 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
EXPECT_EQ(overrideBuffer2, overrideBuffer3);
}
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsProjectionSpace) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideDisplaySpace =
+ layerState1->getOutputLayer()->getState().overrideInfo.displaySpace;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+
+ EXPECT_EQ(overrideDisplaySpace.bounds,
+ Rect(mOutputState.framebufferSpace.bounds.getWidth(),
+ mOutputState.framebufferSpace.bounds.getHeight()));
+ EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2));
+ EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideDamageRegion =
+ layerState1->getOutputLayer()->getState().overrideInfo.damageRegion;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+ EXPECT_TRUE(overrideDamageRegion.isRect() &&
+ overrideDamageRegion.bounds() == Rect::INVALID_RECT);
+
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ EXPECT_TRUE(overrideDamageRegion.isRect() && overrideDamageRegion.bounds() == Rect::EMPTY_RECT);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsVisibleRegion) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideVisibleRegion =
+ layerState1->getOutputLayer()->getState().overrideInfo.visibleRegion;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // make all layers inactive
+ mTime += 200ms;
+ expectAllLayersFlattened(layers);
+ Region expectedRegion;
+ expectedRegion.orSelf(Rect(1, 1, 2, 2));
+ expectedRegion.orSelf(Rect(2, 2, 3, 3));
+ EXPECT_TRUE(overrideVisibleRegion.hasSameRects(expectedRegion));
+}
+
TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
auto& layerState1 = mTestLayers[0]->layerState;
const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
@@ -276,7 +359,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -313,7 +396,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -322,7 +405,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -335,7 +418,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -344,7 +427,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -386,7 +469,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -399,7 +482,8 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -411,7 +495,8 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -426,7 +511,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -437,7 +522,8 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 8f235ab..47b1ae8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -24,6 +24,9 @@
#include <gtest/gtest.h>
#include <log/log.h>
+#include "android/hardware_buffer.h"
+#include "compositionengine/LayerFECompositionState.h"
+
namespace android::compositionengine::impl::planner {
namespace {
@@ -48,7 +51,15 @@
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);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.3f, 0.2f);
+const std::string sMetadataKeyOne = std::string("Meta!");
+const std::string sMetadataKeyTwo = std::string("Data!");
+const GenericLayerMetadataEntry sMetadataValueOne = GenericLayerMetadataEntry{
+ .value = std::vector<uint8_t>({1, 2}),
+};
+const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
+ .value = std::vector<uint8_t>({1, 3}),
+};
struct LayerStateTest : public testing::Test {
LayerStateTest() {
@@ -75,6 +86,21 @@
EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
}
+ void verifyUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs) {
+ EXPECT_EQ(lhs.getHash(), rhs.getHash());
+
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), lhs.getDifferingFields(rhs));
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), rhs.getDifferingFields(lhs));
+ }
+
+ void verifyNonUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs,
+ Flags<LayerStateField> fields) {
+ EXPECT_NE(lhs.getHash(), rhs.getHash());
+
+ EXPECT_EQ(fields, lhs.getDifferingFields(rhs));
+ EXPECT_EQ(fields, rhs.getDifferingFields(lhs));
+ }
+
mock::LayerFE mLayerFE;
mock::OutputLayer mOutputLayer;
std::unique_ptr<LayerState> mLayerState;
@@ -129,21 +155,7 @@
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));
-
+ verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
EXPECT_FALSE(mLayerState->compare(*otherLayerState));
EXPECT_FALSE(otherLayerState->compare(*mLayerState));
}
@@ -188,21 +200,7 @@
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));
-
+ verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
EXPECT_FALSE(mLayerState->compare(*otherLayerState));
EXPECT_FALSE(otherLayerState->compare(*mLayerState));
}
@@ -253,20 +251,7 @@
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));
-
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::DisplayFrame);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
}
@@ -337,34 +322,12 @@
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));
-
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ 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;
@@ -380,7 +343,6 @@
setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
layerFECompositionStateTwo);
Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
- EXPECT_EQ(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
}
@@ -399,21 +361,8 @@
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));
+ // A buffer is not a unique field, but the assertions are the same.
+ verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
// Buffers are explicitly excluded from comparison
EXPECT_FALSE(mLayerState->compare(*otherLayerState));
@@ -453,19 +402,7 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SourceCrop);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -504,19 +441,7 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ZOrder);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -555,19 +480,8 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ LayerStateField::BufferTransform);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -606,19 +520,7 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlendMode);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -657,24 +559,61 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Alpha);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
}
+TEST_F(LayerStateTest, updateLayerMetadata) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::LayerMetadata), updates);
+}
+
+TEST_F(LayerStateTest, compareLayerMetadata) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::LayerMetadata);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getVisibleRegion) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ outputLayerCompositionState.visibleRegion = sRegionOne;
+ LayerFECompositionState layerFECompositionState;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->getVisibleRegion().hasSameRects(sRegionOne));
+}
+
TEST_F(LayerStateTest, updateVisibleRegion) {
OutputLayerCompositionState outputLayerCompositionState;
outputLayerCompositionState.visibleRegion = sRegionOne;
@@ -708,19 +647,7 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::VisibleRegion);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -759,19 +686,65 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, 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, updatePixelFormat) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer1");
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer2");
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer) |
+ Flags<LayerStateField>(LayerStateField::PixelFormat),
+ updates);
+}
+
+TEST_F(LayerStateTest, comparePixelFormat) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer1");
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.buffer =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ "buffer2");
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+ Flags<LayerStateField>(LayerStateField::PixelFormat));
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -814,19 +787,48 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, 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, updateSurfaceDamage) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.surfaceDamage = sRegionOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.surfaceDamage = sRegionTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SurfaceDamage), updates);
+}
+
+TEST_F(LayerStateTest, compareSurfaceDamage) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.surfaceDamage = sRegionOne;
+ setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ mock::LayerFE newLayerFE;
+ OutputLayerCompositionState outputLayerCompositionStateTwo;
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.surfaceDamage = sRegionTwo;
+ setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SurfaceDamage);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -865,19 +867,7 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SidebandStream);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -916,19 +906,7 @@
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));
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SolidColor);
EXPECT_TRUE(mLayerState->compare(*otherLayerState));
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f4a2a3f..b7b2cc6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -354,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 7156613..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;
@@ -217,6 +219,8 @@
const bool mIsPrimary;
std::optional<DeviceProductInfo> mDeviceProductInfo;
+
+ std::vector<ui::Hdr> mOverrideHdrTypes;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 1bf43da..fd70988 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -20,11 +20,7 @@
#undef LOG_TAG
#define LOG_TAG "HwcComposer"
-
-#include <log/log.h>
-
-#include <algorithm>
-#include <cinttypes>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "ComposerHal.h"
@@ -32,6 +28,11 @@
#include <gui/BufferQueue.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cinttypes>
namespace android {
@@ -492,6 +493,7 @@
Error Composer::presentDisplay(Display display, int* outPresentFence)
{
+ ATRACE_NAME("HwcPresentDisplay");
mWriter.selectDisplay(display);
mWriter.presentDisplay();
@@ -586,6 +588,7 @@
Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
uint32_t* outNumRequests)
{
+ ATRACE_NAME("HwcValidateDisplay");
mWriter.selectDisplay(display);
mWriter.validateDisplay();
@@ -601,13 +604,14 @@
Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
- mWriter.selectDisplay(display);
- mWriter.presentOrvalidateDisplay();
+ ATRACE_NAME("HwcPresentOrValidateDisplay");
+ mWriter.selectDisplay(display);
+ mWriter.presentOrvalidateDisplay();
- Error error = execute();
- if (error != Error::NONE) {
- return error;
- }
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
mReader.takePresentOrValidateStage(display, state);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index f7fc162..8d685cf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -76,14 +76,14 @@
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
- const auto limitedSize = limitFramebufferSize(size);
+ const auto limitedSize = limitSize(size);
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
}
void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
- const auto limitedSize = limitFramebufferSize(newSize);
+ const auto limitedSize = limitSize(newSize);
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
}
@@ -179,19 +179,23 @@
}
}
-ui::Size FramebufferSurface::limitFramebufferSize(const ui::Size& size) {
+ui::Size FramebufferSurface::limitSize(const ui::Size& size) {
+ return limitSizeInternal(size, mMaxSize);
+}
+
+ui::Size FramebufferSurface::limitSizeInternal(const ui::Size& size, const ui::Size& maxSize) {
ui::Size limitedSize = size;
bool wasLimited = false;
- if (size.width > mMaxSize.width && mMaxSize.width != 0) {
+ if (size.width > maxSize.width && maxSize.width != 0) {
const float aspectRatio = static_cast<float>(size.width) / size.height;
- limitedSize.height = mMaxSize.width / aspectRatio;
- limitedSize.width = mMaxSize.width;
+ limitedSize.height = maxSize.width / aspectRatio;
+ limitedSize.width = maxSize.width;
wasLimited = true;
}
- if (size.height > mMaxSize.height && mMaxSize.height != 0) {
+ if (limitedSize.height > maxSize.height && maxSize.height != 0) {
const float aspectRatio = static_cast<float>(size.width) / size.height;
- limitedSize.height = mMaxSize.height;
- limitedSize.width = mMaxSize.height * aspectRatio;
+ limitedSize.height = maxSize.height;
+ limitedSize.width = maxSize.height * aspectRatio;
wasLimited = true;
}
ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 5d1e131..3123351 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -55,15 +55,20 @@
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
private:
+ friend class FramebufferSurfaceTest;
+
+ // Limits the width and height by the maximum width specified.
+ ui::Size limitSize(const ui::Size&);
+
+ // Used for testing purposes.
+ static ui::Size limitSizeInternal(const ui::Size&, const ui::Size& maxSize);
+
virtual ~FramebufferSurface() { }; // this class cannot be overloaded
virtual void freeBufferLocked(int slotIndex);
virtual void dumpLocked(String8& result, const char* prefix) const;
- // Limits the width and height by the maximum width specified in the constructor.
- ui::Size limitFramebufferSize(const ui::Size&);
-
// nextBuffer waits for and then latches the next buffer from the
// BufferQueue and releases the previously latched buffer to the
// BufferQueue. The new buffer is returned in the 'buffer' argument.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index ccfaa76..b73d032 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -322,8 +322,11 @@
}
bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, false);
- return mDisplayData.at(displayId).hwcDisplay->isConnected();
+ if (mDisplayData.count(displayId)) {
+ return mDisplayData.at(displayId).hwcDisplay->isConnected();
+ }
+
+ return false;
}
std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
@@ -964,8 +967,10 @@
std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo;
const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo);
if (error != hardware::graphics::composer::V2_4::Error::NONE) {
- ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
- toString(error).c_str(), static_cast<int32_t>(error));
+ if (error != hardware::graphics::composer::V2_4::Error::UNSUPPORTED) {
+ ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
+ toString(error).c_str(), static_cast<int32_t>(error));
+ }
return;
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b1dff8d..6485265 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -348,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) {
@@ -570,13 +575,9 @@
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
// Finish late, Present late
- if (displayFrameJankType == JankType::None) {
- // Display frame is not janky, so purely app's fault
- mJankType |= JankType::AppDeadlineMissed;
- } else {
- // Propagate DisplayFrame's jankType if it is janky
- mJankType |= displayFrameJankType;
- }
+ mJankType |= JankType::AppDeadlineMissed;
+ // Propagate DisplayFrame's jankType if it is janky
+ mJankType |= displayFrameJankType;
}
}
}
@@ -828,10 +829,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();
@@ -869,6 +874,10 @@
mSurfaceFlingerActuals.endTime = actualEndTime;
}
+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) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 3cf35f0..3f04592 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -180,6 +180,7 @@
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.
@@ -292,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
@@ -375,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.
@@ -444,8 +446,8 @@
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;
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 8ad805b..178c531 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -62,10 +62,9 @@
mFrameRecords[mOffset].actualPresentTime = presentTime;
}
-void FrameTracker::setActualPresentFence(
- std::shared_ptr<FenceTime>&& readyFence) {
+void FrameTracker::setActualPresentFence(const std::shared_ptr<FenceTime>& readyFence) {
Mutex::Autolock lock(mMutex);
- mFrameRecords[mOffset].actualPresentFence = std::move(readyFence);
+ mFrameRecords[mOffset].actualPresentFence = readyFence;
mNumFences++;
}
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 35382be..bc412ae 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -66,7 +66,7 @@
// setActualPresentFence sets the fence that is used to get the time
// at which the current frame became visible to the user.
- void setActualPresentFence(std::shared_ptr<FenceTime>&& fence);
+ void setActualPresentFence(const std::shared_ptr<FenceTime>& fence);
// setDisplayRefreshPeriod sets the display refresh period in nanoseconds.
// This is used to compute frame presentation duration statistics relative
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
new file mode 100644
index 0000000..c06e300
--- /dev/null
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HdrLayerInfoReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <utils/Trace.h>
+
+#include "HdrLayerInfoReporter.h"
+
+namespace android {
+
+void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
+ ATRACE_CALL();
+ std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke;
+ {
+ std::scoped_lock lock(mMutex);
+ toInvoke.reserve(mListeners.size());
+ for (auto& [key, it] : mListeners) {
+ if (it.lastInfo != info) {
+ it.lastInfo = info;
+ toInvoke.push_back(it.listener);
+ }
+ }
+ }
+
+ for (const auto& listener : toInvoke) {
+ ATRACE_NAME("invoking onHdrLayerInfoChanged");
+ listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags);
+ }
+}
+
+void HdrLayerInfoReporter::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mMutex);
+ mListeners.erase(who);
+}
+
+void HdrLayerInfoReporter::addListener(const sp<gui::IHdrLayerInfoListener>& listener) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, HdrLayerInfo{}});
+}
+
+void HdrLayerInfoReporter::removeListener(const sp<gui::IHdrLayerInfoListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
new file mode 100644
index 0000000..671395f
--- /dev/null
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <android/gui/IHdrLayerInfoListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+namespace android {
+
+class HdrLayerInfoReporter final : public IBinder::DeathRecipient {
+public:
+ struct HdrLayerInfo {
+ int32_t numberOfHdrLayers = 0;
+ int32_t maxW = 0;
+ int32_t maxH = 0;
+ int32_t flags = 0;
+
+ bool operator==(const HdrLayerInfo& other) const {
+ return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
+ maxH == other.maxH && flags == other.flags;
+ }
+
+ bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
+ };
+
+ HdrLayerInfoReporter() = default;
+ ~HdrLayerInfoReporter() final = default;
+
+ // Dispatches updated layer fps values for the registered listeners
+ // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+ // must be held when calling this method.
+ void dispatchHdrLayerInfo(const HdrLayerInfo& info) EXCLUDES(mMutex);
+
+ // Override for IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
+
+ // Registers an Fps listener that listens to fps updates for the provided layer
+ void addListener(const sp<gui::IHdrLayerInfoListener>& listener) EXCLUDES(mMutex);
+ // Deregisters an Fps listener
+ void removeListener(const sp<gui::IHdrLayerInfoListener>& listener) EXCLUDES(mMutex);
+
+ bool hasListeners() const EXCLUDES(mMutex) {
+ std::scoped_lock lock(mMutex);
+ return !mListeners.empty();
+ }
+
+private:
+ mutable std::mutex mMutex;
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+
+ struct TrackedListener {
+ sp<gui::IHdrLayerInfoListener> listener;
+ HdrLayerInfo lastInfo;
+ };
+
+ std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index cd3e8ad..829b916 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,7 +116,8 @@
mCurrentState.bufferTransform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
- mCurrentState.acquireFence = new Fence(-1);
+ mCurrentState.acquireFence = sp<Fence>::make(-1);
+ mCurrentState.acquireFenceTime = std::make_shared<FenceTime>(mCurrentState.acquireFence);
mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
mCurrentState.hdrMetadata.validTypes = 0;
mCurrentState.surfaceDamageRegion = Region::INVALID_REGION;
@@ -1948,32 +1949,6 @@
return removeResult;
}
-void Layer::reparentChildren(const sp<Layer>& newParent) {
- for (const sp<Layer>& child : mCurrentChildren) {
- newParent->addChild(child);
- }
- mCurrentChildren.clear();
- updateTreeHasFrameRateVote();
-}
-
-bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
- sp<Handle> handle = nullptr;
- sp<Layer> newParent = nullptr;
- if (newParentHandle == nullptr) {
- return false;
- }
- handle = static_cast<Handle*>(newParentHandle.get());
- newParent = handle->owner.promote();
- if (newParent == nullptr) {
- ALOGE("Unable to promote Layer handle");
- return false;
- }
-
- reparentChildren(newParent);
-
- return true;
-}
-
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
@@ -2289,8 +2264,13 @@
return parentAlpha * getDrawingState().backgroundBlurRadius;
}
-const std::vector<BlurRegion>& Layer::getBlurRegions() const {
- return getDrawingState().blurRegions;
+const std::vector<BlurRegion> Layer::getBlurRegions() const {
+ auto regionsCopy(getDrawingState().blurRegions);
+ int layerAlpha = getAlpha();
+ for (auto& region : regionsCopy) {
+ region.alpha = region.alpha * layerAlpha;
+ }
+ return regionsCopy;
}
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
@@ -2312,8 +2292,8 @@
}
}
const float radius = getDrawingState().cornerRadius;
- return radius > 0 && getCrop(getDrawingState()).isValid()
- ? RoundedCornerState(getCrop(getDrawingState()).toFloatRect(), radius)
+ return radius > 0 && getCroppedBufferSize(getDrawingState()).isValid()
+ ? RoundedCornerState(getCroppedBufferSize(getDrawingState()).toFloatRect(), radius)
: RoundedCornerState();
}
@@ -2534,14 +2514,22 @@
return mRemovedFromCurrentState;
}
+ui::Transform Layer::getInputTransform() const {
+ return getTransform();
+}
+
+Rect Layer::getInputBounds() const {
+ return getCroppedBufferSize(getDrawingState());
+}
+
void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) {
// Transform layer size to screen space and inset it by surface insets.
// If this is a portal window, set the touchableRegion to the layerBounds.
Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
- ? getBufferSize(getDrawingState())
+ ? getInputBounds()
: info.touchableRegion.getBounds();
if (!layerBounds.isValid()) {
- layerBounds = getCroppedBufferSize(getDrawingState());
+ layerBounds = getInputBounds();
}
if (!layerBounds.isValid()) {
@@ -2554,7 +2542,7 @@
return;
}
- ui::Transform layerToDisplay = getTransform();
+ ui::Transform layerToDisplay = getInputTransform();
// Transform that takes window coordinates to unrotated display coordinates
ui::Transform t = toPhysicalDisplay * layerToDisplay;
int32_t xSurfaceInset = info.surfaceInset;
@@ -2867,6 +2855,18 @@
}
}
+scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+ switch (strategy) {
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+ return Seamlessness::OnlySeamless;
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+ return Seamlessness::SeamedAndSeamless;
+ default:
+ LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+ return Seamlessness::Default;
+ }
+}
+
bool Layer::getPrimaryDisplayOnly() const {
const State& s(mDrawingState);
if (s.flags & layer_state_t::eLayerSkipScreenshot) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1c5d6ec..64986af 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -52,6 +52,7 @@
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "RenderArea.h"
+#include "Scheduler/LayerInfo.h"
#include "Scheduler/Seamlessness.h"
#include "SurfaceFlinger.h"
#include "SurfaceTracing.h"
@@ -141,60 +142,8 @@
float radius = 0.0f;
};
- // FrameRateCompatibility specifies how we should interpret the frame rate associated with
- // the layer.
- enum class FrameRateCompatibility {
- Default, // Layer didn't specify any specific handling strategy
-
- Exact, // Layer needs the exact frame rate.
-
- ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
- // content properly. Any other value will result in a pull down.
-
- NoVote, // Layer doesn't have any requirements for the refresh rate and
- // should not be considered when the display refresh rate is determined.
- };
-
- // Encapsulates the frame rate and compatibility of the layer. This information will be used
- // when the display refresh rate is determined.
- struct FrameRate {
- using Seamlessness = scheduler::Seamlessness;
-
- Fps rate;
- FrameRateCompatibility type;
- Seamlessness seamlessness;
-
- FrameRate()
- : rate(0),
- type(FrameRateCompatibility::Default),
- seamlessness(Seamlessness::Default) {}
- FrameRate(Fps rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
- : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {}
-
- bool operator==(const FrameRate& other) const {
- return rate.equalsWithMargin(other.rate) && type == other.type &&
- seamlessness == other.seamlessness;
- }
-
- bool operator!=(const FrameRate& other) const { return !(*this == other); }
-
- // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
- // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
- static FrameRateCompatibility convertCompatibility(int8_t compatibility);
-
- private:
- static Seamlessness getSeamlessness(Fps rate, bool shouldBeSeamless) {
- if (!rate.isValid()) {
- // Refresh rate of 0 is a special value which should reset the vote to
- // its default value.
- return Seamlessness::Default;
- } else if (shouldBeSeamless) {
- return Seamlessness::OnlySeamless;
- } else {
- return Seamlessness::SeamedAndSeamless;
- }
- }
- };
+ using FrameRate = scheduler::LayerInfo::FrameRate;
+ using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
struct State {
Geometry active_legacy;
@@ -262,6 +211,7 @@
sp<GraphicBuffer> buffer;
client_cache_t clientCacheId;
sp<Fence> acquireFence;
+ std::shared_ptr<FenceTime> acquireFenceTime;
HdrMetadata hdrMetadata;
Region surfaceDamageRegion;
int32_t api;
@@ -462,7 +412,6 @@
// Used only to set BufferStateLayer state
virtual bool setTransform(uint32_t /*transform*/) { return false; };
virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { 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*/,
@@ -688,8 +637,6 @@
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
const char* getDebugName() const override;
- bool reparentChildren(const sp<IBinder>& newParentHandle);
- void reparentChildren(const sp<Layer>& newParent);
bool setShadowRadius(float shadowRadius);
// Before color management is introduced, contents on Android have to be
@@ -1076,6 +1023,9 @@
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
bool usingRelativeZ(LayerVector::StateSet) const;
+ virtual ui::Transform getInputTransform() const;
+ virtual Rect getInputBounds() const;
+
// SyncPoints which will be signaled when the correct frame is at the head
// of the queue and dropped after the frame has been latched. Protected by
// mLocalSyncPointMutex.
@@ -1241,7 +1191,7 @@
float mEffectiveShadowRadius = 0.f;
// A list of regions on this layer that should have blurs.
- const std::vector<BlurRegion>& getBlurRegions() const;
+ const std::vector<BlurRegion> getBlurRegions() const;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 1d00cc3..7a3e433 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -231,8 +231,14 @@
void RefreshRateOverlay::setViewport(ui::Size viewport) {
Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
- mLayer->setFrame(frame);
+ layer_state_t::matrix22_t matrix;
+ matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
+ matrix.dtdx = 0;
+ matrix.dtdy = 0;
+ matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
+ mLayer->setMatrix(matrix, true);
+ mLayer->setPosition(frame.left, frame.top);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ea92ad8..f4bc2a1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,13 +39,13 @@
namespace {
-bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
// Layers with an explicit vote are always kept active
- if (layer.getFrameRateForLayerTree().rate.isValid()) {
+ if (info.getSetFrameRateVote().rate.isValid()) {
return true;
}
- return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+ return info.isVisible() && info.getLastUpdatedTime() >= threshold;
}
bool traceEnabled() {
@@ -58,11 +58,7 @@
return atoi(value);
}
-void trace(const wp<Layer>& weak, const LayerInfo& info, LayerHistory::LayerVoteType type,
- int fps) {
- const auto layer = weak.promote();
- if (!layer) return;
-
+void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) {
const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
};
@@ -75,7 +71,7 @@
traceType(LayerHistory::LayerVoteType::Min, 1);
traceType(LayerHistory::LayerVoteType::Max, 1);
- ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
} // namespace
@@ -88,11 +84,27 @@
LayerHistory::~LayerHistory() = default;
void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
- auto info = std::make_unique<LayerInfo>(layer->getName(), type);
+ auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
}
+void LayerHistory::deregisterLayer(Layer* layer) {
+ std::lock_guard lock(mLock);
+
+ const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+ [layer](const auto& pair) { return pair.first == layer; });
+ LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+ const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
+ if (i < mActiveLayersEnd) {
+ mActiveLayersEnd--;
+ }
+ const size_t last = mLayerInfos.size() - 1;
+ std::swap(mLayerInfos[i], mLayerInfos[last]);
+ mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
+}
+
void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
LayerUpdateType updateType) {
std::lock_guard lock(mLock);
@@ -102,7 +114,15 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now, updateType, mModeChangePending);
+ const auto layerProps = LayerInfo::LayerProps{
+ .visible = layer->isVisible(),
+ .bounds = layer->getBounds(),
+ .transform = layer->getTransform(),
+ .setFrameRateVote = layer->getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ };
+
+ info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -119,15 +139,10 @@
partitionLayers(now);
for (const auto& [layer, info] : activeLayers()) {
- const auto strong = layer.promote();
- if (!strong) {
- continue;
- }
-
- const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+ const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
- ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
- frameRateSelectionPriority, layerFocused ? "" : "not");
+ ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
+ layerFocused ? "" : "not");
const auto vote = info->getRefreshRateVote(now);
// Skip NoVote layer as those don't have any requirements
@@ -136,18 +151,18 @@
}
// Compute the layer's position on the screen
- const Rect bounds = Rect(strong->getBounds());
- const ui::Transform transform = strong->getTransform();
+ const Rect bounds = Rect(info->getBounds());
+ const ui::Transform transform = info->getTransform();
constexpr bool roundOutwards = true;
Rect transformed = transform.transform(bounds, roundOutwards);
const float layerArea = transformed.getWidth() * transformed.getHeight();
float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back({strong->getName(), strong->getOwnerUid(), vote.type, vote.fps,
+ summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
vote.seamlessness, weight, layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, *info, vote.type, vote.fps.getIntValue());
+ trace(*info, vote.type, vote.fps.getIntValue());
}
}
@@ -160,11 +175,11 @@
// Collect expired and inactive layers after active layers.
size_t i = 0;
while (i < mActiveLayersEnd) {
- auto& [weak, info] = mLayerInfos[i];
- if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+ auto& [layerUnsafe, info] = mLayerInfos[i];
+ if (isLayerActive(*info, threshold)) {
i++;
// Set layer vote if set
- const auto frameRate = layer->getFrameRateForLayerTree();
+ const auto frameRate = info->getSetFrameRateVote();
const auto voteType = [&]() {
switch (frameRate.type) {
case Layer::FrameRateCompatibility::Default:
@@ -179,7 +194,7 @@
}();
if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) {
- const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+ const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
} else {
info->resetLayerVote();
@@ -188,24 +203,12 @@
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
+ trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
}
info->onLayerInactive(now);
std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
-
- // Collect expired layers after inactive layers.
- size_t end = mLayerInfos.size();
- while (i < end) {
- if (mLayerInfos[i].first.promote()) {
- i++;
- } else {
- std::swap(mLayerInfos[i], mLayerInfos[--end]);
- }
- }
-
- mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
}
void LayerHistory::clear() {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 05ecc70..82f6c39 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -70,13 +70,15 @@
Summary summarize(nsecs_t now);
void clear();
+
+ void deregisterLayer(Layer*);
std::string dump() const;
private:
friend LayerHistoryTest;
friend TestableScheduler;
- using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+ using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
using LayerInfos = std::vector<LayerPair>;
struct ActiveLayers {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 4b4cdae..989bf4e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -37,17 +37,20 @@
const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
bool LayerInfo::sTraceEnabled = false;
-LayerInfo::LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote)
+LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
+ LayerHistory::LayerVoteType defaultVote)
: mName(name),
+ mOwnerUid(ownerUid),
mDefaultVote(defaultVote),
mLayerVote({defaultVote, Fps(0.0f)}),
mRefreshRateHistory(name) {}
void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingModeChange) {
+ bool pendingModeChange, LayerProps props) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
+ mLayerProps = props;
switch (updateType) {
case LayerUpdateType::AnimationTX:
mLastAnimationTime = std::max(lastPresentTime, now);
@@ -232,6 +235,8 @@
if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
mLastRefreshRate.animatingOrInfrequent = true;
+ // Infrequent layers vote for mininal refresh rate for
+ // battery saving purposes and also to prevent b/135718869.
return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 40c0214..ba03c89 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,6 +16,7 @@
#pragma once
+#include <ui/Transform.h>
#include <utils/Timers.h>
#include <chrono>
@@ -65,22 +66,84 @@
Seamlessness seamlessness = Seamlessness::Default;
};
+ // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+ // the layer.
+ enum class FrameRateCompatibility {
+ Default, // Layer didn't specify any specific handling strategy
+
+ Exact, // Layer needs the exact frame rate.
+
+ ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+ // content properly. Any other value will result in a pull down.
+
+ NoVote, // Layer doesn't have any requirements for the refresh rate and
+ // should not be considered when the display refresh rate is determined.
+ };
+
+ // Encapsulates the frame rate and compatibility of the layer. This information will be used
+ // when the display refresh rate is determined.
+ struct FrameRate {
+ using Seamlessness = scheduler::Seamlessness;
+
+ Fps rate;
+ FrameRateCompatibility type;
+ Seamlessness seamlessness;
+
+ FrameRate()
+ : rate(0),
+ type(FrameRateCompatibility::Default),
+ seamlessness(Seamlessness::Default) {}
+ FrameRate(Fps rate, FrameRateCompatibility type,
+ Seamlessness seamlessness = Seamlessness::OnlySeamless)
+ : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+
+ bool operator==(const FrameRate& other) const {
+ return rate.equalsWithMargin(other.rate) && type == other.type &&
+ seamlessness == other.seamlessness;
+ }
+
+ bool operator!=(const FrameRate& other) const { return !(*this == other); }
+
+ // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
+ // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
+ static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+ static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
+
+ private:
+ static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
+ if (!rate.isValid()) {
+ // Refresh rate of 0 is a special value which should reset the vote to
+ // its default value.
+ return Seamlessness::Default;
+ }
+ return seamlessness;
+ }
+ };
+
static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
sRefreshRateConfigs = &refreshRateConfigs;
}
- LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote);
+ LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
+ struct LayerProps {
+ bool visible = false;
+ FloatRect bounds;
+ ui::Transform transform;
+ FrameRate setFrameRateVote;
+ int32_t frameRateSelectionPriority = -1;
+ };
+
// Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingModeChange);
+ bool pendingModeChange, LayerProps props);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -94,12 +157,24 @@
// Resets the layer vote to its default.
void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
+ std::string getName() const { return mName; }
+
+ uid_t getOwnerUid() const { return mOwnerUid; }
+
LayerVote getRefreshRateVote(nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
+ FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
+ bool isVisible() const { return mLayerProps.visible; }
+ int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
+
+ FloatRect getBounds() const { return mLayerProps.bounds; }
+
+ ui::Transform getTransform() const { return mLayerProps.transform; }
+
// Returns a C string for tracing a vote
const char* getTraceTag(LayerHistory::LayerVoteType type) const;
@@ -193,6 +268,7 @@
bool isFrameTimeValid(const FrameTimeData&) const;
const std::string mName;
+ const uid_t mOwnerUid;
// Used for sanitizing the heuristic data. If two frames are less than
// this period apart from each other they'll be considered as duplicates.
@@ -217,6 +293,8 @@
static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+ LayerProps mLayerProps;
+
RefreshRateHistory mRefreshRateHistory;
mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 91e8043..b062acd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -215,7 +215,7 @@
int explicitExactOrMultipleVoteLayers = 0;
int explicitExact = 0;
float maxExplicitWeight = 0;
- int seamedLayers = 0;
+ int seamedFocusedLayers = 0;
for (const auto& layer : layers) {
switch (layer.vote) {
case LayerVoteType::NoVote:
@@ -243,8 +243,8 @@
break;
}
- if (layer.seamlessness == Seamlessness::SeamedAndSeamless) {
- seamedLayers++;
+ if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+ seamedFocusedLayers++;
}
}
@@ -329,12 +329,11 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = seamedLayers > 0
+ const bool isInPolicyForDefault = seamedFocusedLayers > 0
? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
: scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
- if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
- !layer.focused) {
+ if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->toString().c_str(),
mCurrentRefreshRate->toString().c_str());
@@ -499,11 +498,7 @@
// Now that we scored all the refresh rates we need to pick the one that got the highest
// score.
const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
-
- // If the nest refresh rate is the current one, we don't have an override
- if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) {
- frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
- }
+ frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
}
return frameRateOverrides;
@@ -839,11 +834,6 @@
return static_cast<int>(numPeriodsRounded);
}
-int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const {
- std::lock_guard lock(mLock);
- return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate);
-}
-
void RefreshRateConfigs::dump(std::string& result) const {
std::lock_guard lock(mLock);
base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2bc22b4..ee89149 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -324,8 +324,10 @@
bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; }
- // Returns a divider for the current refresh rate
- int getRefreshRateDivider(Fps frameRate) const EXCLUDES(mLock);
+ // Return the display refresh rate divider to match the layer
+ // frame rate, or 0 if the display refresh rate is not a multiple of the
+ // layer refresh rate.
+ static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
using UidToFrameRateOverride = std::map<uid_t, Fps>;
// Returns the frame rate override for each uid.
@@ -373,11 +375,6 @@
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
- // Return the display refresh rate divider to match the layer
- // frame rate, or 0 if the display refresh rate is not a multiple of the
- // layer refresh rate.
- static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
-
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 9813270..8426737 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -235,12 +235,7 @@
return true;
}
- const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
- if (divider <= 1) {
- return true;
- }
-
- return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
+ return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -354,6 +349,10 @@
std::lock_guard<std::mutex> lock(mFeatureStateLock);
// Cache the last reported modes for primary display.
mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod};
+
+ // Invalidate content based refresh rate selection so it could be calculated
+ // again for the new refresh rate.
+ mFeatures.contentRequirements.clear();
}
onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
}
@@ -581,6 +580,12 @@
mLayerHistory->registerLayer(layer, voteType);
}
+void Scheduler::deregisterLayer(Layer* layer) {
+ if (mLayerHistory) {
+ mLayerHistory->deregisterLayer(layer);
+ }
+}
+
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
if (mLayerHistory) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0e9eba7..d4932e7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -129,6 +129,7 @@
void registerLayer(Layer*);
void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
void setModeChangePending(bool pending);
+ void deregisterLayer(Layer*);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 7cca206..028f7a6 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -30,6 +30,7 @@
#include <algorithm>
#include <chrono>
#include <sstream>
+#include "RefreshRateConfigs.h"
#undef LOG_TAG
#define LOG_TAG "VSyncPredictor"
@@ -225,13 +226,14 @@
}
/*
- * Returns whether a given vsync timestamp is in phase with a vsync divider.
- * For example, if the vsync timestamps are (16,32,48,64):
- * isVSyncInPhase(16, 2) = true
- * isVSyncInPhase(32, 2) = false
- * isVSyncInPhase(48, 2) = true
+ * Returns whether a given vsync timestamp is in phase with a frame rate.
+ * If the frame rate is not a divider of the refresh rate, it is always considered in phase.
+ * For example, if the vsync timestamps are (16.6,33.3,50.0,66.6):
+ * isVSyncInPhase(16.6, 30) = true
+ * isVSyncInPhase(33.3, 30) = false
+ * isVSyncInPhase(50.0, 30) = true
*/
-bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
struct VsyncError {
nsecs_t vsyncTimestamp;
float error;
@@ -239,11 +241,13 @@
bool operator<(const VsyncError& other) const { return error < other.error; }
};
+ std::lock_guard lock(mMutex);
+ const auto divider =
+ RefreshRateConfigs::getFrameRateDivider(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
if (divider <= 1 || timePoint == 0) {
return true;
}
- std::lock_guard lock(mMutex);
const nsecs_t period = mRateMap[mIdealPeriod].slope;
const nsecs_t justBeforeTimePoint = timePoint - period / 2;
const nsecs_t dividedPeriod = mIdealPeriod / divider;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 381cf81..40e6944 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -64,7 +64,7 @@
VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
- bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex);
+ bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
void dump(std::string& result) const final EXCLUDES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2cd9b3d..95750ad 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -17,6 +17,7 @@
#pragma once
#include <utils/Timers.h>
+#include "Fps.h"
#include "VSyncDispatch.h"
namespace android::scheduler {
@@ -69,12 +70,12 @@
virtual bool needsMoreSamples() const = 0;
/*
- * Checks if a vsync timestamp is in phase for a given divider.
+ * Checks if a vsync timestamp is in phase for a frame rate
*
* \param [in] timePoint A vsync timestamp
- * \param [in] divider The divider to check for
+ * \param [in] frameRate The frame rate to check for
*/
- virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0;
+ virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 31097aa..02579c6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -112,6 +112,7 @@
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "HdrLayerInfoReporter.h"
#include "Layer.h"
#include "LayerRenderArea.h"
#include "LayerVector.h"
@@ -285,6 +286,7 @@
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
+const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
const String16 sDump("android.permission.DUMP");
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
@@ -753,6 +755,8 @@
getRenderEngine().primeCache();
}
+ getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
+
// Inform native graphics APIs whether the present timestamp is supported:
const bool presentFenceReliable =
@@ -1287,6 +1291,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,
@@ -1477,6 +1496,47 @@
.get();
}
+status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+ const auto displayId = display->getId();
+ sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
+ if (!hdrInfoReporter) {
+ hdrInfoReporter = sp<HdrLayerInfoReporter>::make();
+ }
+ hdrInfoReporter->addListener(listener);
+ return OK;
+}
+
+status_t SurfaceFlinger::removeHdrLayerInfoListener(
+ const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+ const auto displayId = display->getId();
+ sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
+ if (hdrInfoReporter) {
+ hdrInfoReporter->removeListener(listener);
+ }
+ return OK;
+}
+
status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
Boost powerBoost = static_cast<Boost>(boostId);
@@ -1646,7 +1706,7 @@
}));
}
-sp<Fence> SurfaceFlinger::previousFrameFence() {
+SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
@@ -1656,9 +1716,9 @@
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
ATRACE_CALL();
- const sp<Fence>& fence = previousFrameFence();
+ const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
- if (fence == Fence::NO_FENCE) {
+ if (fence == FenceTime::NO_FENCE) {
return false;
}
@@ -1669,9 +1729,9 @@
}
nsecs_t SurfaceFlinger::previousFramePresentTime() {
- const sp<Fence>& fence = previousFrameFence();
+ const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
- if (fence == Fence::NO_FENCE) {
+ if (fence == FenceTime::NO_FENCE) {
return Fence::SIGNAL_TIME_INVALID;
}
@@ -1754,10 +1814,6 @@
if (frameMissed) {
mFrameMissedCount++;
mTimeStats->incrementMissedFrames();
- if (mMissedFrameJankCount == 0) {
- mMissedFrameJankStart = systemTime();
- }
- mMissedFrameJankCount++;
}
if (hwcFrameMissed) {
@@ -1789,37 +1845,6 @@
}
}
- // Our jank window is always at least 100ms since we missed a
- // frame...
- static constexpr nsecs_t kMinJankyDuration =
- std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
- // ...but if it's larger than 1s then we missed the trace cutoff.
- static constexpr nsecs_t kMaxJankyDuration =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
- nsecs_t jankDurationToUpload = -1;
- // If we're in a user build then don't push any atoms
- if (!mIsUserBuild && mMissedFrameJankCount > 0) {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- // Only report jank when the display is on, as displays in DOZE
- // power mode may operate at a different frame rate than is
- // reported in their config, which causes noticeable (but less
- // severe) jank.
- if (display && display->getPowerMode() == hal::PowerMode::ON) {
- const nsecs_t currentTime = systemTime();
- const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
- if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
- jankDurationToUpload = jankDuration;
- }
-
- // We either reported a jank event or we missed the trace
- // window, so clear counters here.
- if (jankDuration > kMinJankyDuration) {
- mMissedFrameJankCount = 0;
- mMissedFrameJankStart = 0;
- }
- }
- }
-
if (mTracingEnabledChanged) {
mTracingEnabled = mTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -1866,7 +1891,6 @@
refreshNeeded |= mRepaintEverything;
if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
- mLastJankDuration = jankDurationToUpload;
// Signal a refresh if a transaction modified the window state,
// a new buffer was latched, or if HWC has requested a full
// repaint
@@ -2102,16 +2126,18 @@
getBE().mDisplayTimeline.updateSignalTimes();
mPreviousPresentFences[1] = mPreviousPresentFences[0];
- mPreviousPresentFences[0] =
+ mPreviousPresentFences[0].fence =
display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
- auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
- getBE().mDisplayTimeline.push(presentFenceTime);
+ mPreviousPresentFences[0].fenceTime =
+ std::make_shared<FenceTime>(mPreviousPresentFences[0].fence);
+
+ getBE().mDisplayTimeline.push(mPreviousPresentFences[0].fenceTime);
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
- mFrameTimeline->setSfPresent(systemTime(),
- std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+ mFrameTimeline->setSfPresent(systemTime(), mPreviousPresentFences[0].fenceTime,
+ glCompositionDoneFenceTime != FenceTime::NO_FENCE);
nsecs_t dequeueReadyTime = systemTime();
for (const auto& layer : mLayersWithQueuedFrames) {
@@ -2124,7 +2150,7 @@
// be sampled a little later than when we started doing work for this frame,
// but that should be okay since updateCompositorTiming has snapping logic.
updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
- presentFenceTime);
+ mPreviousPresentFences[0].fenceTime);
CompositorTiming compositorTiming;
{
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -2132,26 +2158,74 @@
}
mDrawingState.traverse([&](Layer* layer) {
- const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
- presentFenceTime, compositorTiming);
+ const bool frameLatched =
+ layer->onPostComposition(display, glCompositionDoneFenceTime,
+ mPreviousPresentFences[0].fenceTime, compositorTiming);
if (frameLatched) {
recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
}
});
+ std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
+ hdrInfoListeners;
{
Mutex::Autolock lock(mStateLock);
if (mFpsReporter) {
mFpsReporter->dispatchLayerFps();
}
+ hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
+ for (auto& [key, value] : mHdrLayerInfoListeners) {
+ if (value && value->hasListeners()) {
+ auto listenersDisplay = getDisplayById(key);
+ if (listenersDisplay) {
+ hdrInfoListeners.emplace_back(listenersDisplay->getCompositionDisplay(), value);
+ }
+ }
+ }
}
- mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
+ for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
+ HdrLayerInfoReporter::HdrLayerInfo info;
+ int32_t maxArea = 0;
+ mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+ if (layer->isVisible() &&
+ compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) {
+ bool isHdr = false;
+ switch (layer->getDataSpace()) {
+ case ui::Dataspace::BT2020:
+ case ui::Dataspace::BT2020_HLG:
+ case ui::Dataspace::BT2020_PQ:
+ case ui::Dataspace::BT2020_ITU:
+ case ui::Dataspace::BT2020_ITU_HLG:
+ case ui::Dataspace::BT2020_ITU_PQ:
+ isHdr = true;
+ break;
+ default:
+ isHdr = false;
+ break;
+ }
+
+ if (isHdr) {
+ info.numberOfHdrLayers++;
+ auto bufferRect = layer->getCompositionState()->geomBufferSize;
+ int32_t area = bufferRect.width() * bufferRect.height();
+ if (area > maxArea) {
+ maxArea = area;
+ info.maxW = bufferRect.width();
+ info.maxH = bufferRect.height();
+ }
+ }
+ }
+ });
+ listener->dispatchHdrLayerInfo(info);
+ }
+
+ mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
mTransactionCallbackInvoker.sendCallbacks();
if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
- presentFenceTime->isValid()) {
- mScheduler->addPresentFence(presentFenceTime);
+ mPreviousPresentFences[0].fenceTime->isValid()) {
+ mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);
}
const bool isDisplayConnected =
@@ -2166,9 +2240,8 @@
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
- if (presentFenceTime->isValid()) {
- mAnimFrameTracker.setActualPresentFence(
- std::move(presentFenceTime));
+ if (mPreviousPresentFences[0].fenceTime->isValid()) {
+ mAnimFrameTracker.setActualPresentFence(mPreviousPresentFences[0].fenceTime);
} else if (isDisplayConnected) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
@@ -2187,20 +2260,12 @@
mTimeStats->incrementClientCompositionReusedFrames();
}
- mTimeStats->setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime);
const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
- if (mLastJankDuration > 0) {
- ATRACE_NAME("Jank detected");
- const int32_t jankyDurationMillis = mLastJankDuration / (1000 * 1000);
- android::util::stats_write(android::util::DISPLAY_JANK_REPORTED, jankyDurationMillis,
- mMissedFrameJankCount);
- mLastJankDuration = -1;
- }
-
if (isDisplayConnected && !display->isPoweredOn()) {
return;
}
@@ -2618,6 +2683,7 @@
if (display->isPrimary()) {
mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
+ getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
}
}
@@ -3251,8 +3317,12 @@
}
}
- if (const auto display = getDefaultDisplayDeviceLocked()) {
- lbc->updateTransformHint(display->getTransformHint());
+ if (const auto token = getInternalDisplayTokenLocked()) {
+ const ssize_t index = mCurrentState.displays.indexOfKey(token);
+ if (index >= 0) {
+ const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+ lbc->updateTransformHint(ui::Transform::toRotationFlags(state.orientation));
+ }
}
if (outTransformHint) {
*outTransformHint = lbc->getTransformHint();
@@ -3303,7 +3373,7 @@
// states) around outside the scope of the lock
std::vector<const TransactionState> transactions;
// Layer handles that have transactions with buffers that are ready to be applied.
- std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> pendingBuffers;
+ std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
{
Mutex::Autolock _l(mStateLock);
{
@@ -3319,10 +3389,13 @@
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
- pendingBuffers)) {
+ bufferLayersReadyToPresent)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
+ transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ bufferLayersReadyToPresent.insert(state.surface);
+ });
transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
}
@@ -3343,14 +3416,17 @@
auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
- if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+ if (pendingTransactions ||
+ !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
- pendingBuffers) ||
- pendingTransactions) {
+ bufferLayersReadyToPresent)) {
mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
+ transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ bufferLayersReadyToPresent.insert(state.surface);
+ });
transactions.emplace_back(std::move(transaction));
}
mTransactionQueue.pop();
@@ -3405,28 +3481,28 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
- std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+ bufferLayersReadyToPresent) const {
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;
+ return false;
}
if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
ATRACE_NAME("!isVsyncValid");
- ready = false;
+ return false;
}
// If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
// present time of this transaction.
if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) {
ATRACE_NAME("frameIsEarly");
- ready = false;
+ return false;
}
for (const ComposerState& state : states) {
@@ -3434,13 +3510,14 @@
const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
if (acquireFenceChanged && s.acquireFence &&
s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
- ready = false;
+ ATRACE_NAME("fence unsignaled");
+ return false;
}
sp<Layer> layer = nullptr;
if (s.surface) {
layer = fromHandleLocked(s.surface).promote();
- } else if (acquireFenceChanged) {
+ } else if (s.hasBufferChanges()) {
ALOGW("Transaction with buffer, but no Layer?");
continue;
}
@@ -3450,18 +3527,18 @@
ATRACE_NAME(layer->getName().c_str());
- if (acquireFenceChanged) {
+ if (s.hasBufferChanges()) {
// 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();
+ const bool hasPendingBuffer =
+ bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
ATRACE_NAME("hasPendingBuffer");
- ready = false;
+ return false;
}
- pendingBuffers.insert(s.surface);
}
}
- return ready;
+ return true;
}
void SurfaceFlinger::queueTransaction(TransactionState& state) {
@@ -3531,13 +3608,6 @@
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
@@ -3566,6 +3636,11 @@
permissions, hasListenerCallbacks,
listenerCallbacks, originPid,
originUid, transactionId};
+
+ // Check for incoming buffer updates and increment the pending buffer count.
+ state.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ mBufferCountTracker.increment(state.surface->localBinder());
+ });
queueTransaction(state);
// Check the pending state to make sure the transaction is synchronous.
@@ -3904,11 +3979,6 @@
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
- if (what & layer_state_t::eReparentChildren) {
- if (layer->reparentChildren(s.reparentSurfaceControl->getHandle())) {
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
if (what & layer_state_t::eTransformChanged) {
if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
}
@@ -3919,9 +3989,6 @@
if (what & layer_state_t::eCropChanged) {
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eFrameChanged) {
- if (layer->setFrame(s.orientedDisplaySpaceRect)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eAcquireFenceChanged) {
if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
}
@@ -3967,13 +4034,16 @@
}
}
if (what & layer_state_t::eFrameRateChanged) {
- if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
- "SurfaceFlinger::setClientStateLocked", privileged) &&
- layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate),
- Layer::FrameRate::convertCompatibility(
- s.frameRateCompatibility),
- s.shouldBeSeamless))) {
- flags |= eTraversalNeeded;
+ if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
+ "SurfaceFlinger::setClientStateLocked", privileged)) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
+ const auto strategy =
+ Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
+
+ if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+ flags |= eTraversalNeeded;
+ }
}
}
if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -5013,6 +5083,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:
@@ -5029,9 +5100,11 @@
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
- // necessary permission dynamically. Don't use the permission cache for this check.
- bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN;
+ // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
+ // which acquire the necessary permission dynamically. Don't use the permission cache
+ // for this check.
+ bool usePermissionCache =
+ code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5090,6 +5163,20 @@
// This is not sensitive information, so should not require permission control.
return OK;
}
+ case ADD_HDR_LAYER_INFO_LISTENER:
+ case REMOVE_HDR_LAYER_INFO_LISTENER: {
+ // TODO (b/183985553): Should getting & setting brightness be part of this...?
+ // codes that require permission check
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_GRAPHICS) &&
+ !PermissionCache::checkPermission(sControlDisplayBrightness, pid, uid)) {
+ ALOGE("Permission Denial: can't control brightness pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ return OK;
+ }
case ADD_FPS_LISTENER:
case REMOVE_FPS_LISTENER:
case ADD_REGION_SAMPLING_LISTENER:
@@ -5651,6 +5738,15 @@
return getDisplayByLayerStack(displayOrLayerStack);
}
+sp<DisplayDevice> SurfaceFlinger::getDisplayById(DisplayId displayId) const {
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getId() == displayId) {
+ return display;
+ }
+ }
+ return nullptr;
+}
+
sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
for (const auto& [token, display] : mDisplays) {
if (display->getLayerStack() == layerStack) {
@@ -6306,7 +6402,7 @@
return fromHandleLocked(handle);
}
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) {
+wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
BBinder* b = nullptr;
if (handle) {
b = handle->localBinder();
@@ -6327,6 +6423,7 @@
}
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+ mScheduler->deregisterLayer(layer);
mNumLayers--;
removeFromOffscreenLayers(layer);
}
@@ -6378,8 +6475,9 @@
}
status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) {
- if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "SurfaceFlinger::setFrameRate")) {
return BAD_VALUE;
}
@@ -6391,10 +6489,12 @@
ALOGE("Attempt to set frame rate on a layer that no longer exists");
return BAD_VALUE;
}
+ const auto strategy =
+ Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
if (layer->setFrameRate(
Layer::FrameRate(Fps{frameRate},
Layer::FrameRate::convertCompatibility(compatibility),
- shouldBeSeamless))) {
+ strategy))) {
setTransactionFlags(eTraversalNeeded);
}
} else {
@@ -6548,6 +6648,15 @@
return NO_ERROR;
}
+void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+ std::function<void(const layer_state_t&)> visitor) {
+ for (const auto& state : states) {
+ if (state.state.hasBufferChanges() && (state.state.surface)) {
+ visitor(state.state);
+ }
+ }
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8d2f66d..b3da61e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -90,6 +90,7 @@
class Client;
class EventThread;
class FpsReporter;
+class HdrLayerInfoReporter;
class HWComposer;
struct SetInputWindowsListener;
class IGraphicBufferProducer;
@@ -329,7 +330,7 @@
// Otherwise, returns a weak reference so that callers off the main-thread
// won't accidentally hold onto the last strong reference.
wp<Layer> fromHandle(const sp<IBinder>& handle);
- wp<Layer> fromHandleLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
+ wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
// Inherit from ClientCache::ErasedRecipient
void bufferErased(const client_cache_t& clientCacheId) override;
@@ -536,6 +537,8 @@
originUid(originUid),
id(transactionId) {}
+ void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
FrameTimelineInfo frameTimelineInfo;
Vector<ComposerState> states;
Vector<DisplayState> displays;
@@ -642,6 +645,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;
@@ -682,11 +687,15 @@
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken,
const gui::DisplayBrightness& brightness) override;
+ status_t addHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override;
+ status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
+ const sp<gui::IHdrLayerInfoListener>& listener) override;
status_t notifyPowerBoost(int32_t boostId) override;
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) override;
+ int8_t compatibility, int8_t changeFrameRateStrategy) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
@@ -839,8 +848,8 @@
bool transactionIsReadyToBeApplied(
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);
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+ bufferLayersReadyToPresent) const REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
@@ -906,6 +915,7 @@
bool grayscale, ScreenCaptureResults&);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
+ sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
@@ -1011,9 +1021,14 @@
bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock);
+ struct FenceWithFenceTime {
+ sp<Fence> fence = Fence::NO_FENCE;
+ std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
+ };
+
// Gets the fence for the previous frame.
// Must be called on the main thread.
- sp<Fence> previousFrameFence();
+ FenceWithFenceTime previousFrameFence();
// Whether the previous frame has not yet been presented to the display.
// If graceTimeMs is positive, this method waits for at most the provided
@@ -1191,7 +1206,7 @@
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> mLayersWithQueuedFrames;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
- std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE};
+ std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
// True if in the previous frame at least one layer was composed via the GPU.
bool mHadClientComposition = false;
// True if in the previous frame at least one layer was composed via HW Composer.
@@ -1372,18 +1387,14 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- // Fields tracking the current jank event: when it started and how many
- // janky frames there are.
- nsecs_t mMissedFrameJankStart = 0;
- int32_t mMissedFrameJankCount = 0;
- // Positive if jank should be uploaded in postComposition
- nsecs_t mLastJankDuration = -1;
-
int mFrameRateFlexibilityTokenCount = 0;
sp<IBinder> mDebugFrameRateFlexibilityToken;
BufferCountTracker mBufferCountTracker;
+
+ std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
+ GUARDED_BY(mStateLock);
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 8a3be9f..b49562a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -401,13 +401,6 @@
overrideChange->set_parent_id(parentId);
}
-void SurfaceInterceptor::addReparentChildrenLocked(Transaction* transaction, int32_t layerId,
- int32_t parentId) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- ReparentChildrenChange* overrideChange(change->mutable_reparent_children());
- overrideChange->set_parent_id(parentId);
-}
-
void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
int32_t parentId, int z) {
SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
@@ -486,10 +479,6 @@
: nullptr;
addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
}
- if (state.what & layer_state_t::eReparentChildren) {
- addReparentChildrenLocked(transaction, layerId,
- getLayerIdFromHandle(state.reparentSurfaceControl->getHandle()));
- }
if (state.what & layer_state_t::eRelativeLayerChanged) {
addRelativeParentLocked(transaction, layerId,
getLayerIdFromHandle(
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 3e27e83..d2cbf40 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -176,7 +176,6 @@
uint32_t transactionFlags, int originPid, int originUid,
uint64_t transactionId);
void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
- void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
int z);
void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 974ae84..2094972 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -89,12 +89,15 @@
return byteString;
}
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
- int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+ float refreshRate,
+ TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+ TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
util::ProtoOutputStream proto;
proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
- proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
- proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+ proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+ static_cast<int>(frameRateCompatibility));
+ proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
std::string byteString;
proto.serializeToString(&byteString);
@@ -229,7 +232,10 @@
mStatsDelegate->statsEventWriteInt32(
event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
- std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0);
+ std::string frameRateVoteBytes =
+ frameRateVoteToProtoByteString(layer->setFrameRateVote.frameRate,
+ layer->setFrameRateVote.frameRateCompatibility,
+ layer->setFrameRateVote.seamlessness);
mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
frameRateVoteBytes.size()); // set_frame_rate_vote
std::string appDeadlineMissedBytes =
@@ -468,8 +474,10 @@
}
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
- std::optional<Fps> renderRate) {
+ std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
ATRACE_CALL();
+ ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
@@ -501,6 +509,9 @@
displayStats.stats[layerKey].uid = uid;
displayStats.stats[layerKey].layerName = layerName;
}
+ if (frameRateVote.frameRate > 0.0f) {
+ displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
+ }
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
@@ -724,7 +735,8 @@
}
void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate) {
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -743,12 +755,13 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
}
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
- Fps displayRefreshRate, std::optional<Fps> renderRate) {
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -768,7 +781,7 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
}
static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index fd112b9..a87b7cb 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -50,6 +50,8 @@
class TimeStats {
public:
+ using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
+
virtual ~TimeStats() = default;
// Called once boot has been finished to perform additional capabilities,
@@ -110,10 +112,12 @@
// SetPresent{Time, Fence} are not expected to be called in the critical
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
- Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -307,10 +311,11 @@
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate) override;
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
- std::optional<Fps> renderRate) override;
+ std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
@@ -334,7 +339,8 @@
AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
- std::optional<Fps> renderRate);
+ std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d116b02..a7e7db2 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -91,6 +91,37 @@
return result;
}
+std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
+ switch (compatibility) {
+ case FrameRateCompatibility::Undefined:
+ return "Undefined";
+ case FrameRateCompatibility::Default:
+ return "Default";
+ case FrameRateCompatibility::ExactOrMultiple:
+ return "ExactOrMultiple";
+ }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
+ switch (seamlessness) {
+ case Seamlessness::Undefined:
+ return "Undefined";
+ case Seamlessness::ShouldBeSeamless:
+ return "ShouldBeSeamless";
+ case Seamlessness::NotRequired:
+ return "NotRequired";
+ }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString() const {
+ std::string result;
+ StringAppendF(&result, "frameRate = %.2f\n", frameRate);
+ StringAppendF(&result, "frameRateCompatibility = %s\n",
+ toString(frameRateCompatibility).c_str());
+ StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+ return result;
+}
+
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -104,6 +135,8 @@
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
result.append("Jank payload for this layer:\n");
result.append(jankPayload.toString());
+ result.append("SetFrateRate vote for this layer:\n");
+ result.append(setFrameRateVote.toString());
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
const float averageTime = iter->second.averageTime();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5ee28ce..2b37ffe 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -55,6 +55,28 @@
std::string toString() const;
};
+ struct SetFrameRateVote {
+ float frameRate = 0;
+
+ // Needs to be in sync with atoms.proto
+ enum class FrameRateCompatibility {
+ Undefined = 0,
+ Default = 1,
+ ExactOrMultiple = 2,
+ } frameRateCompatibility = FrameRateCompatibility::Undefined;
+
+ // Needs to be in sync with atoms.proto
+ enum class Seamlessness {
+ Undefined = 0,
+ ShouldBeSeamless = 1,
+ NotRequired = 2,
+ } seamlessness = Seamlessness::Undefined;
+
+ static std::string toString(FrameRateCompatibility);
+ static std::string toString(Seamlessness);
+ std::string toString() const;
+ };
+
class TimeStatsLayer {
public:
uid_t uid;
@@ -67,6 +89,7 @@
int32_t lateAcquireFrames = 0;
int32_t badDesiredPresentFrames = 0;
JankPayload jankPayload;
+ SetFrameRateVote setFrameRateVote;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 2b8424c..9686523 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -89,13 +89,47 @@
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4);
+ // The binder threadpool we start will inherit sched policy and priority
+ // of (this) creating thread. We want the binder thread pool to have
+ // SCHED_FIFO policy and priority 1 (lowest RT priority)
+ // Once the pool is created we reset this thread's priority back to
+ // original.
+ int newPriority = 0;
+ int origPolicy = sched_getscheduler(0);
+ struct sched_param origSchedParam;
+
+ int errorInPriorityModification = sched_getparam(0, &origSchedParam);
+ if (errorInPriorityModification == 0) {
+ int policy = SCHED_FIFO;
+ newPriority = sched_get_priority_min(policy);
+
+ struct sched_param param;
+ param.sched_priority = newPriority;
+
+ errorInPriorityModification = sched_setscheduler(0, policy, ¶m);
+ }
+
// start the thread pool
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
+ // Reset current thread's policy and priority
+ if (errorInPriorityModification == 0) {
+ errorInPriorityModification = sched_setscheduler(0, origPolicy, &origSchedParam);
+ } else {
+ ALOGE("Failed to set SurfaceFlinger binder threadpool priority to SCHED_FIFO");
+ }
+
// instantiate surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
+ // Set the minimum policy of surfaceflinger node to be SCHED_FIFO.
+ // So any thread with policy/priority lower than {SCHED_FIFO, 1}, will run
+ // at least with SCHED_FIFO policy and priority 1.
+ if (errorInPriorityModification == 0) {
+ flinger->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
+ }
+
setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
set_sched_policy(0, SP_FOREGROUND);
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 03f8e1a..47a150d 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -296,12 +296,12 @@
BufferGenerator::BufferGenerator()
: mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
- const float width = 1000.0;
- const float height = 1000.0;
+ mBufferSize.set(1000.0, 1000.0);
auto setBufferWithContext =
std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
- mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+ mSurfaceManager->initialize(mBufferSize.width, mBufferSize.height, HAL_PIXEL_FORMAT_RGBA_8888,
+ setBufferWithContext);
if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
@@ -309,7 +309,9 @@
if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
mProgram->use();
- mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+ mProgram->bindVec4(0,
+ vec4{mBufferSize.width, mBufferSize.height, 1.0f / mBufferSize.width,
+ 1.0f / mBufferSize.height});
mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
glEnableVertexAttribArray(0);
@@ -372,6 +374,10 @@
return NO_ERROR;
}
+ui::Size BufferGenerator::getSize() {
+ return mBufferSize;
+}
+
// static
void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
void* bufferGenerator) {
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
index a3ffe86..f7d548b 100644
--- a/services/surfaceflinger/tests/BufferGenerator.h
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -37,6 +37,7 @@
/* Static callback that sets the fence on a particular instance */
static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+ ui::Size getSize();
private:
bool mInitialized = false;
@@ -53,6 +54,7 @@
using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
Epoch mEpoch = std::chrono::steady_clock::now();
+ ui::Size mBufferSize;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index f470eda..af00ec7 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -149,7 +149,6 @@
t.reparent(blurLayer, mParentLayer);
t.setBackgroundBlurRadius(blurLayer, blurRadius);
t.setCrop(blurLayer, blurRect);
- t.setFrame(blurLayer, blurRect);
t.setAlpha(blurLayer, 0.0f);
t.show(blurLayer);
});
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index a8647c3..9fa3d4c 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -161,7 +161,6 @@
Color::RED);
transaction->setLayerStack(mSurfaceControl, 0)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .setFrame(mSurfaceControl, Rect(0, 0, width, height))
.setBuffer(mSurfaceControl, gb)
.setAcquireFence(mSurfaceControl, fence)
.show(mSurfaceControl)
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 158801a..011ff70 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -164,7 +164,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
@@ -184,7 +187,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -203,7 +209,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -238,7 +247,10 @@
return;
}
- transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(-100, -100, 100, 100));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -263,8 +275,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -290,8 +309,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -318,8 +344,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
@@ -405,8 +438,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -491,7 +531,11 @@
}
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expected;
expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
@@ -523,8 +567,16 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -564,8 +616,16 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -606,8 +666,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -661,8 +728,15 @@
return;
}
- transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+
+ transaction2.merge(std::move(transaction1)).apply();
ExpectedResult expected;
expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -682,7 +756,10 @@
return;
}
- transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(32, 32, 64, 64));
+ transaction2.merge(std::move(transaction1)).apply();
expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -762,7 +839,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ ui::Size bufferSize = getBufferSize();
+ TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
ExpectedResult expectedResult;
expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -781,7 +861,10 @@
return;
}
- transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ TransactionUtils::setFrame(transaction, layer,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.apply();
}
EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
}
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 7505e6e..53d230a 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -204,11 +204,7 @@
Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
- Transaction()
- .setFrame(layerR, Rect(0, 0, 32, 32))
- .setFrame(layerG, Rect(16, 16, 48, 48))
- .setRelativeLayer(layerG, layerR, 1)
- .apply();
+ Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
break;
default:
ASSERT_FALSE(true) << "Unsupported layer type";
@@ -260,10 +256,9 @@
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
Transaction()
- .setFrame(layerR, Rect(0, 0, 32, 32))
- .setFrame(layerG, Rect(8, 8, 40, 40))
+ .setPosition(layerG, 8, 8)
.setRelativeLayer(layerG, layerR, 3)
- .setFrame(layerB, Rect(16, 16, 48, 48))
+ .setPosition(layerB, 16, 16)
.setLayer(layerB, mLayerZBase + 2)
.apply();
break;
@@ -388,7 +383,6 @@
Transaction()
.setTransparentRegionHint(layer, Region(top))
.setBuffer(layer, buffer)
- .setFrame(layer, Rect(0, 0, 32, 32))
.apply();
{
SCOPED_TRACE("top transparent");
@@ -447,7 +441,7 @@
// check that transparent region hint is bound by the layer size
Transaction()
.setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
- .setFrame(layerR, Rect(16, 16, 48, 48))
+ .setPosition(layerR, 16, 16)
.setLayer(layerR, mLayerZBase + 1)
.apply();
ASSERT_NO_FATAL_FAILURE(
@@ -477,8 +471,7 @@
Transaction()
.setAlpha(layer1, 0.25f)
.setAlpha(layer2, 0.75f)
- .setFrame(layer1, Rect(0, 0, 32, 32))
- .setFrame(layer2, Rect(16, 0, 48, 32))
+ .setPosition(layer2, 16, 0)
.setLayer(layer2, mLayerZBase + 1)
.apply();
break;
@@ -573,7 +566,7 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
expectedColor = fillColor;
}
- Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+ Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
break;
default:
GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
@@ -849,42 +842,39 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
Color::BLUE, Color::WHITE));
- Transaction()
- .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
- .setFrame(layer, Rect(0, 0, 32, 32))
- .apply();
+ Transaction().setPosition(layer, 32, 32).setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).apply();
{
SCOPED_TRACE("IDENTITY");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ getScreenCapture()->expectQuadrant(Rect(32, 32, 64, 64), Color::RED, Color::GREEN,
Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
{
SCOPED_TRACE("FLIP_H");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 32, 32, 64), Color::GREEN, Color::RED,
+ Color::WHITE, Color::BLUE);
}
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
{
SCOPED_TRACE("FLIP_V");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(32, 0, 64, 32), Color::BLUE, Color::WHITE,
+ Color::RED, Color::GREEN);
}
Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
{
SCOPED_TRACE("ROT_90");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 32, 32, 64), Color::BLUE, Color::RED,
+ Color::WHITE, Color::GREEN);
}
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
{
SCOPED_TRACE("SCALE");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(32, 32, 96, 96), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, 1 /* tolerance */);
}
}
@@ -955,8 +945,8 @@
Transaction().setCrop(layer, crop).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(crop, Color::RED);
+ shot->expectBorder(crop, Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
@@ -986,13 +976,13 @@
{
SCOPED_TRACE("empty rect");
Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
{
SCOPED_TRACE("negative rect");
Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
@@ -1016,8 +1006,6 @@
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
- Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
-
Transaction().setBuffer(layer, buffer).apply();
// Partially out of bounds in the negative (upper left) direction
@@ -1025,8 +1013,8 @@
{
SCOPED_TRACE("out of bounds, negative (upper left) direction");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 16), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 16), Color::BLACK);
}
// Partially out of bounds in the positive (lower right) direction
@@ -1034,8 +1022,8 @@
{
SCOPED_TRACE("out of bounds, positive (lower right) direction");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ shot->expectColor(Rect(0, 16, 32, 64), Color::RED);
+ shot->expectBorder(Rect(0, 16, 32, 64), Color::BLACK);
}
// Fully out of buffer space bounds
@@ -1043,9 +1031,7 @@
{
SCOPED_TRACE("Fully out of bounds");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
- shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 64, 64), Color::BLACK);
}
}
@@ -1068,12 +1054,11 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- const Rect frame(32, 32, 64, 64);
const Rect crop(8, 8, 24, 24);
- Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
+ Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply();
auto shot = getScreenCapture();
- shot->expectColor(frame, Color::RED);
- shot->expectBorder(frame, Color::BLACK);
+ shot->expectColor(Rect(40, 40, 56, 56), Color::RED);
+ shot->expectBorder(Rect(40, 40, 56, 56), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
@@ -1121,7 +1106,10 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
const Rect frame(8, 8, 24, 24);
- Transaction().setFrame(layer, frame).apply();
+ Transaction t;
+ TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), frame);
+ t.apply();
+
auto shot = getScreenCapture();
shot->expectColor(frame, Color::RED);
shot->expectBorder(frame, Color::BLACK);
@@ -1133,16 +1121,23 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ Transaction t;
{
SCOPED_TRACE("empty rect");
- Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+ TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), Rect(8, 8, 8, 8));
+ t.apply();
+
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
{
SCOPED_TRACE("negative rect");
- Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
- getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), Rect(8, 8, 0, 0));
+ t.apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+ shot->expectBorder(Rect(0, 0, 8, 8), Color::BLACK);
}
}
@@ -1152,10 +1147,10 @@
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
- // A parentless layer will default to a frame with the same size as the buffer
+ // A layer with a buffer will have a computed size that matches the buffer size.
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 10, 10), Color::RED);
+ shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
@@ -1163,17 +1158,16 @@
ASSERT_NO_FATAL_FAILURE(
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
- Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
Transaction().reparent(child, parent).apply();
- // A layer will default to the frame of its parent
+ // A layer with a buffer will have a computed size that matches the buffer size.
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 0, 10, 10), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -1183,14 +1177,14 @@
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
Transaction().reparent(child, parent).apply();
- // A layer will default to the frame of its parent
+ // A layer with a buffer will have a computed size that matches the buffer size.
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 0, 10, 10), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -1199,11 +1193,10 @@
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
std::this_thread::sleep_for(500ms);
- Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
+ Transaction().setPosition(layer, 16, 16).apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
@@ -1215,18 +1208,20 @@
ASSERT_NO_FATAL_FAILURE(
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(
- child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
Transaction().reparent(child, parent).apply();
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
- Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
+ Rect childDst(0, 16, 32, 32);
+ Transaction t;
+ TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst);
+ t.apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
- shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+ shot->expectColor(childDst, Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
@@ -1238,8 +1233,8 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
@@ -1252,8 +1247,8 @@
{
SCOPED_TRACE("set buffer 1");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
@@ -1261,8 +1256,8 @@
{
SCOPED_TRACE("set buffer 2");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
@@ -1270,8 +1265,8 @@
{
SCOPED_TRACE("set buffer 3");
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
}
@@ -1286,7 +1281,6 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
- Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
{
SCOPED_TRACE("set layer 1 buffer red");
auto shot = getScreenCapture();
@@ -1295,7 +1289,6 @@
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
- Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
{
SCOPED_TRACE("set layer 2 buffer blue");
auto shot = getScreenCapture();
@@ -1350,8 +1343,8 @@
Color color = colors[idx % colors.size()];
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
idx++;
}
@@ -1383,8 +1376,8 @@
Color color = colors[idx % colors.size()];
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
idx++;
}
@@ -1416,8 +1409,8 @@
Color color = colors[idx % colors.size()];
auto shot = screenshot();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), color);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
if (idx == 0) {
buffers[0].clear();
@@ -1435,7 +1428,6 @@
Color::BLUE, Color::WHITE));
Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
.apply();
@@ -1452,7 +1444,6 @@
Color::BLUE, Color::WHITE));
Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
.apply();
@@ -1469,7 +1460,6 @@
Color::BLUE, Color::WHITE));
Transaction()
- .setFrame(layer, Rect(0, 0, 32, 32))
.setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
.apply();
@@ -1518,8 +1508,8 @@
Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
@@ -1534,8 +1524,8 @@
Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
@@ -1552,8 +1542,8 @@
Transaction().setBuffer(layer, buffer).setHdrMetadata(layer, hdrMetadata).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
@@ -1570,8 +1560,8 @@
Transaction().setBuffer(layer, buffer).setSurfaceDamageRegion(layer, region).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
@@ -1586,8 +1576,8 @@
Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
auto shot = getScreenCapture();
- shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
- shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 87c7b7d..0bc8fe7 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -244,6 +244,11 @@
return bufferGenerator.get(outBuffer, outFence);
}
+ static ui::Size getBufferSize() {
+ static BufferGenerator bufferGenerator;
+ return bufferGenerator.getSize();
+ }
+
sp<SurfaceComposerClient> mClient;
bool deviceSupportsBlurs() {
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index ac5e297..edf55ea 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -196,17 +196,7 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
- if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
- Transaction()
- .setCornerRadius(layer, cornerRadius)
- .setCrop(layer, Rect(0, 0, size, size))
- .apply();
- } else {
- Transaction()
- .setCornerRadius(layer, cornerRadius)
- .setFrame(layer, Rect(0, 0, size, size))
- .apply();
- }
+ Transaction().setCornerRadius(layer, cornerRadius).apply();
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
@@ -234,19 +224,13 @@
ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
- auto transaction = Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setCrop(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(parent, Rect(0, 0, size, size));
- } else {
- transaction.setFrame(parent, Rect(0, 0, size, size));
- }
- transaction.apply();
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, 0, size)
+ // Rotate by half PI
+ .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f)
+ .apply();
{
const uint8_t bottom = size - 1;
@@ -275,21 +259,12 @@
ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
- if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
- Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setCrop(parent, Rect(0, 0, size, size))
- .reparent(child, parent)
- .setPosition(child, 0, size / 2)
- .apply();
- } else {
- Transaction()
- .setCornerRadius(parent, cornerRadius)
- .setFrame(parent, Rect(0, 0, size, size))
- .reparent(child, parent)
- .setFrame(child, Rect(0, size / 2, size, size))
- .apply();
- }
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, 0, size / 2)
+ .apply();
+
{
const uint8_t bottom = size - 1;
const uint8_t right = size - 1;
@@ -331,12 +306,9 @@
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();
{
@@ -352,7 +324,6 @@
.setLayer(blurLayer, mLayerZBase + 3)
.setBackgroundBlurRadius(blurLayer, blurRadius)
.setCrop(blurLayer, blurRect)
- .setFrame(blurLayer, blurRect)
.setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
.setAlpha(blurLayer, 0.0f)
.apply();
@@ -435,10 +406,8 @@
Transaction()
.setLayer(left, mLayerZBase + 1)
- .setFrame(left, {0, 0, size, size})
.setLayer(right, mLayerZBase + 2)
.setPosition(right, size, 0)
- .setFrame(right, {size, 0, size * 2, size})
.apply();
{
@@ -457,7 +426,6 @@
.setAlpha(blurParent, 0.5)
.setLayer(blur, mLayerZBase + 4)
.setBackgroundBlurRadius(blur, size) // set the blur radius to the size of one rect
- .setFrame(blur, {0, 0, size * 2, size})
.reparent(blur, blurParent)
.apply();
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e4a1f66..39d9206 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -449,48 +449,16 @@
{
mCapture = screenshot();
- // Child and BG blended.
- mCapture->checkPixel(0, 0, 127, 127, 0);
+ // Child and BG blended. See b/175352694 for tolerance.
+ mCapture->expectColor(Rect(0, 0, 1, 1), Color{127, 127, 0, 255}, 1);
}
asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
{
mCapture = screenshot();
- // Child and BG blended.
- mCapture->checkPixel(0, 0, 95, 64, 95);
- }
-}
-
-TEST_F(ChildLayerTest, ReparentChildren) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction(
- [&](Transaction& t) { t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl); });
-
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- // In reparenting we should have exposed the entire foreground surface.
- mCapture->expectFGColor(74, 74);
- // And the child layer should now begin at 10, 10 (since the BG
- // layer is at (0, 0)).
- mCapture->expectBGColor(9, 9);
- mCapture->expectChildColor(10, 10);
+ // Child and BG blended. See b/175352694 for tolerance.
+ mCapture->expectColor(Rect(0, 0, 1, 1), Color{95, 64, 95, 255}, 1);
}
}
@@ -539,7 +507,7 @@
asTransaction([&](Transaction& t) {
t.reparent(mChild, nullptr);
- t.reparentChildren(mChild, mFGSurfaceControl);
+ t.reparent(mGrandChild, mFGSurfaceControl);
});
{
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 613b21e..ccf434d 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -195,7 +195,7 @@
createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
mChildLayer.get());
fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
- Transaction().setFrame(bufferStateLayer, Rect(0, 0, 200, 200)).show(bufferStateLayer).apply();
+ Transaction().show(bufferStateLayer).apply();
{
SCOPED_TRACE("Initial Mirror BufferStateLayer");
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index d9cab42..09bd775 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -194,7 +194,6 @@
bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
bool reparentUpdateFound(const SurfaceChange& change, bool found);
bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
- bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
@@ -231,7 +230,6 @@
void deferredTransactionUpdate(Transaction&);
void reparentUpdate(Transaction&);
void relativeParentUpdate(Transaction&);
- void reparentChildrenUpdate(Transaction&);
void shadowRadiusUpdate(Transaction&);
void surfaceCreation(Transaction&);
void displayCreation(Transaction&);
@@ -410,10 +408,6 @@
t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
}
-void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
- t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl);
-}
-
void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
}
@@ -445,7 +439,6 @@
runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
- runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
}
@@ -660,16 +653,6 @@
return found;
}
-bool SurfaceInterceptorTest::reparentChildrenUpdateFound(const SurfaceChange& change, bool found) {
- bool hasId(change.reparent_children().parent_id() == mFGLayerId);
- if (hasId && !found) {
- found = true;
- } else if (hasId && found) {
- []() { FAIL(); }();
- }
- return found;
-}
-
bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
bool foundShadowRadius) {
bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
@@ -738,9 +721,6 @@
case SurfaceChange::SurfaceChangeCase::kReparent:
foundUpdate = reparentUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kReparentChildren:
- foundUpdate = reparentChildrenUpdateFound(change, foundUpdate);
- break;
case SurfaceChange::SurfaceChangeCase::kRelativeParent:
foundUpdate = relativeParentUpdateFound(change, foundUpdate);
break;
@@ -771,7 +751,6 @@
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparentChildren));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
}
@@ -937,11 +916,6 @@
SurfaceChange::SurfaceChangeCase::kReparent);
}
-TEST_F(SurfaceInterceptorTest, InterceptReparentChildrenUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::reparentChildrenUpdate,
- SurfaceChange::SurfaceChangeCase::kReparentChildren);
-}
-
TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
SurfaceChange::SurfaceChangeCase::kRelativeParent);
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 820f248..c081f9b 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1766,30 +1766,6 @@
EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
}
- void Test_ReparentChildren() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- }
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl);
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
// Regression test for b/37673612
void Test_ChildrenWithParentBufferTransform() {
{
@@ -1886,10 +1862,6 @@
Test_LayerAlpha();
}
-TEST_F(ChildLayerTest_2_1, DISABLED_ReparentChildren) {
- Test_ReparentChildren();
-}
-
// Regression test for b/37673612
TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
Test_ChildrenWithParentBufferTransform();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 3c1b9d8..9f94c73 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -55,6 +55,7 @@
"EventThreadTest.cpp",
"FpsReporterTest.cpp",
"FpsTest.cpp",
+ "FramebufferSurfaceTest.cpp",
"FrameTimelineTest.cpp",
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d1385c0..982252f 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -668,9 +668,10 @@
Fps renderRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
- sLayerNameOne, JankType::Unknown,
- -1, -1, 25}));
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+ JankType::Unknown | JankType::AppDeadlineMissed,
+ -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});
@@ -697,7 +698,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
- EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
}
/*
@@ -1699,9 +1700,9 @@
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
- // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+ // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as only
// AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
- // jank to the SurfaceFrame.
+ // jank to the SurfaceFrame along with AppDeadlineMissed.
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -1771,7 +1772,8 @@
EXPECT_EQ(actuals2.presentTime, 60);
EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
- EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+ JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed);
}
TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
diff --git a/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
new file mode 100644
index 0000000..b8df640
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DisplayHardware/FramebufferSurface.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class FramebufferSurfaceTest : public testing::Test {
+public:
+ ui::Size limitSize(const ui::Size& size, const ui::Size maxSize) {
+ return FramebufferSurface::limitSizeInternal(size, maxSize);
+ }
+};
+
+TEST_F(FramebufferSurfaceTest, limitSize) {
+ const ui::Size kMaxSize(1920, 1080);
+ EXPECT_EQ(ui::Size(1920, 1080), limitSize({3840, 2160}, kMaxSize));
+ EXPECT_EQ(ui::Size(1920, 1080), limitSize({1920, 1080}, kMaxSize));
+ EXPECT_EQ(ui::Size(1920, 1012), limitSize({4096, 2160}, kMaxSize));
+ EXPECT_EQ(ui::Size(1080, 1080), limitSize({3840, 3840}, kMaxSize));
+ EXPECT_EQ(ui::Size(1280, 720), limitSize({1280, 720}, kMaxSize));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index fec590e..b67ebca 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -81,8 +81,8 @@
void setDefaultLayerVote(Layer* layer,
LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
- for (auto& [weak, info] : history().mLayerInfos) {
- if (auto strong = weak.promote(); strong && strong.get() == layer) {
+ for (auto& [layerUnsafe, info] : history().mLayerInfos) {
+ if (layerUnsafe == layer) {
info->setDefaultLayerVote(vote);
return;
}
@@ -180,6 +180,7 @@
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
summary = history().summarize(time);
EXPECT_TRUE(history().summarize(time).empty());
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index be76e8f..325fb8f 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -40,7 +40,7 @@
auto calculateAverageFrameTime() { return layerInfo.calculateAverageFrameTime(); }
- LayerInfo layerInfo{"TestLayerInfo", LayerHistory::LayerVoteType::Heuristic};
+ LayerInfo layerInfo{"TestLayerInfo", 0, LayerHistory::LayerVoteType::Heuristic};
};
namespace {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 06f2036..0b70f27 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1182,11 +1182,13 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
-TEST_F(RefreshRateConfigsTest, groupSwitching) {
+TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
/*currentConfigId=*/HWC_CONFIG_ID_60);
+ // The default policy doesn't allow group switching. Verify that no
+ // group switches are performed.
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
@@ -1198,64 +1200,203 @@
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRateConfigs::Policy policy;
policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(90.0f);
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
// Verify that we won't change the group if seamless switch is required.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(90.0f);
layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// Verify that we won't do a seamless switch if we request the same mode as the default
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
layer.desiredRefreshRate = Fps(60.0f);
- layer.name = "60Hz ExplicitDefault";
layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// Verify that if the current config is in another group and there are no layers with
// seamlessness=SeamedAndSeamless we'll go back to the default group.
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
layer.desiredRefreshRate = Fps(60.0f);
- layer.name = "60Hz ExplicitDefault";
layer.seamlessness = Seamlessness::Default;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
+
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// If there's a layer with seamlessness=SeamedAndSeamless, another layer with
// seamlessness=OnlySeamless can't change the mode group.
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
- layer.seamlessness = Seamlessness::OnlySeamless;
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].seamlessness = Seamlessness::OnlySeamless;
+ layers[0].name = "60Hz ExplicitDefault";
+ layers[0].focused = true;
layers.push_back(LayerRequirement{.weight = 0.5f});
- auto& layer2 = layers[layers.size() - 1];
- layer2.vote = LayerVoteType::ExplicitDefault;
- layer2.desiredRefreshRate = Fps(90.0f);
- layer2.name = "90Hz ExplicitDefault";
- layer2.seamlessness = Seamlessness::SeamedAndSeamless;
- layer2.focused = false;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].name = "90Hz ExplicitDefault";
+ layers[1].focused = false;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
- // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
- // seamlessness=Default can't change the mode group.
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
+ // seamlessness=Default can't change the mode group back to the group of the default
+ // mode.
+ // For example, this may happen when a video playback requests and gets a seamed switch,
+ // but another layer (with default seamlessness) starts animating. The animating layer
+ // should not cause a seamed switch.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.1f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].focused = true;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
}
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // Layer with seamlessness=Default can change the mode group if there's a not
+ // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
+ // when in split screen mode the user switches between the two visible applications.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.7f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].focused = false;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60Device,
@@ -1621,29 +1762,35 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
-TEST_F(RefreshRateConfigsTest, getRefreshRateDivider) {
+TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_30);
const auto frameRate = Fps(30.f);
- EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate));
+ Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60);
- EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate));
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72);
- EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate));
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
- EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate));
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
- EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate));
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
- EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f)));
- EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f)));
+ displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 5c8c2d8..0bb7e31 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -122,7 +122,6 @@
void addChild(sp<Layer> layer, sp<Layer> child);
void removeChild(sp<Layer> layer, sp<Layer> child);
- void reparentChildren(sp<Layer> layer, sp<Layer> child);
void commitTransaction();
TestableSurfaceFlinger mFlinger;
@@ -152,10 +151,6 @@
layer.get()->removeChild(child.get());
}
-void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
- parent.get()->reparentChildren(newParent);
-}
-
void SetFrameRateTest::commitTransaction() {
for (auto layer : mLayers) {
layer->pushPendingState();
@@ -433,41 +428,6 @@
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
}
-TEST_P(SetFrameRateTest, SetAndGetReparentChildren) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- reparentChildren(parent, parent2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
testing::Values(std::make_shared<BufferQueueLayerFactory>(),
std::make_shared<BufferStateLayerFactory>(),
@@ -475,18 +435,36 @@
PrintToStringParamName);
TEST_F(SetFrameRateTest, ValidateFrameRate) {
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, "", /*privileged=*/true));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "",
+ /*privileged=*/true));
- EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_FALSE(
- ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_FALSE(
- ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
+ EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
- EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ // Invalid compatibility
+ EXPECT_FALSE(
+ ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ // Invalid change frame rate strategy
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, -1, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, 2, ""));
}
TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
@@ -505,6 +483,13 @@
parent->setFrameRate(FRAME_RATE_VOTE1);
commitTransaction();
+ mFlinger.mutableScheduler()
+ .mutableLayerHistory()
+ ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ mFlinger.mutableScheduler()
+ .mutableLayerHistory()
+ ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+
const auto layerHistorySummary =
mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0);
ASSERT_EQ(2u, layerHistorySummary.size());
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a72ab42..c1f0c4e 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -142,15 +142,16 @@
std::string inputCommand(InputCommand cmd, bool useProto);
- void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+ void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+ TimeStats::SetFrameRateVote frameRateVote);
int32_t genRandomInt32(int32_t begin, int32_t end);
template <size_t N>
void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
- nsecs_t ts) {
+ nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
for (size_t i = 0; i < N; i++, ts += 1000000) {
- setTimeStamp(sequence[i], id, frameNumber, ts);
+ setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
}
}
@@ -234,7 +235,8 @@
return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
}
-void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+ TimeStats::SetFrameRateVote frameRateVote) {
switch (type) {
case TimeStamp::POST:
ASSERT_NO_FATAL_FAILURE(
@@ -254,13 +256,13 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
break;
case TimeStamp::PRESENT:
- ASSERT_NO_FATAL_FAILURE(
- mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
+ kRenderRate0, frameRateVote));
break;
case TimeStamp::PRESENT_FENCE:
- ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber,
- std::make_shared<FenceTime>(ts),
- kRefreshRate0, kRenderRate0));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
+ kRefreshRate0, kRenderRate0, frameRateVote));
break;
default:
ALOGD("Invalid timestamp type");
@@ -411,6 +413,96 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
}
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+ const auto frameRate60 = TimeStats::SetFrameRateVote{
+ .frameRate = 60.0f,
+ .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+ };
+ const auto frameRate90 = TimeStats::SetFrameRateVote{
+ .frameRate = 90.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+ std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "frameRate = 60.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = Default";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = ShouldBeSeamless";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 60.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = Default";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = ShouldBeSeamless";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+ const auto frameRate90 = TimeStats::SetFrameRateVote{
+ .frameRate = 90.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ const auto frameRateDefault = TimeStats::SetFrameRateVote{
+ .frameRate = 0.0f,
+ .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90);
+ std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
// this stat is not in the proto so verify by checking the string dump
constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -936,12 +1028,15 @@
return byteString;
}
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
- int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+ float refreshRate,
+ TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+ TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
util::ProtoOutputStream proto;
proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
- proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
- proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+ proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+ static_cast<int>(frameRateCompatibility));
+ proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
std::string byteString;
proto.serializeToString(&byteString);
@@ -1149,7 +1244,13 @@
for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
}
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ const auto frameRate60 = TimeStats::SetFrameRateVote{
+ .frameRate = 60.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
@@ -1181,7 +1282,10 @@
std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0);
+ std::string expectedFrameRateOverride =
+ frameRateVoteToProtoByteString(frameRate60.frameRate,
+ frameRate60.frameRateCompatibility,
+ frameRate60.seamlessness);
std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3});
{
InSequence seq;
@@ -1455,7 +1559,7 @@
TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
const int32_t ts = genRandomInt32(1, 1000000000);
ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
- setTimeStamp(type, layerId, frameNumber, ts);
+ setTimeStamp(type, layerId, frameNumber, ts, {});
}
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 0af5f30..42b1993 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,7 +52,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, int) const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
void dump(std::string&) const final {}
private:
@@ -89,7 +89,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, int) const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 00cf574..b64cce9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -49,7 +49,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
- MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index a4ddbf4..2a658dd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -469,7 +469,9 @@
for (int divider = 1; divider < maxDivider; divider++) {
for (int i = 0; i < maxPeriods; i++) {
const bool expectedInPhase = (i % divider) == 0;
- EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider))
+ EXPECT_THAT(expectedInPhase,
+ tracker.isVSyncInPhase(mNow + i * mPeriod - bias,
+ Fps::fromPeriodNsecs(divider * mPeriod)))
<< "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
<< (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index b9651ea..5826a9b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -46,7 +46,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
- MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
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/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 3e4a0b8..37b74ed 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -48,10 +48,11 @@
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>));
- MOCK_METHOD5(setPresentFence,
- void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps,
- std::optional<Fps>));
+ MOCK_METHOD6(setPresentTime,
+ void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
+ MOCK_METHOD6(setPresentFence,
+ void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+ SetFrameRateVote));
MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index de98025..5b0c1f3 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -33,7 +33,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
- MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 3cbfed9..8c448e2 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -126,7 +126,7 @@
const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
for (int32_t i = 0; i < width; i++) {
const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
- EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
+ ASSERT_TRUE(std::equal(src, src + 4, expected, colorCompare))
<< "pixel @ (" << x + i << ", " << y + j << "): "
<< "expected (" << color << "), "
<< "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
@@ -161,6 +161,22 @@
ASSERT_EQ(NO_ERROR, s->unlockAndPost());
}
}
+
+ static void setFrame(Transaction& t, const sp<SurfaceControl>& sc, Rect source, Rect dest,
+ int32_t transform = 0) {
+ uint32_t sourceWidth = source.getWidth();
+ uint32_t sourceHeight = source.getHeight();
+
+ if (transform & ui::Transform::ROT_90) {
+ std::swap(sourceWidth, sourceHeight);
+ }
+
+ float dsdx = dest.getWidth() / static_cast<float>(sourceWidth);
+ float dsdy = dest.getHeight() / static_cast<float>(sourceHeight);
+
+ t.setMatrix(sc, dsdx, 0, 0, dsdy);
+ t.setPosition(sc, dest.left, dest.top);
+ }
};
enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 70f9876..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,96 +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(float 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<float> HalController::getResonantFrequency() {
- hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
- return hal->getResonantFrequency();
- };
- return apply(getResonantFrequencyFn, "getResonantFrequency");
-}
-
-HalResult<float> HalController::getQFactor() {
- hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
- return apply(getQFactorFn, "getQFactor");
-}
-
-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<milliseconds> HalController::performComposedEffect(
- const std::vector<CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) {
- hal_fn<milliseconds> 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 baee74f..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());
}
@@ -184,37 +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<float> AidlHalWrapper::getResonantFrequency() {
- std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
- return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
- mResonantFrequency);
-}
-
-HalResult<float> AidlHalWrapper::getQFactor() {
- std::lock_guard<std::mutex> lock(mQFactorMutex);
- return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
-}
-
HalResult<milliseconds> AidlHalWrapper::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
HalResult<Capabilities> capabilities = getCapabilities();
@@ -235,44 +304,32 @@
}
HalResult<milliseconds> AidlHalWrapper::performComposedEffect(
- const std::vector<CompositeEffect>& primitiveEffects,
+ const std::vector<CompositeEffect>& primitives,
const std::function<void()>& completionCallback) {
// This method should always support callbacks, so no need to double check.
auto cb = new HalCallbackWrapper(completionCallback);
+
+ auto durations = getPrimitiveDurations().valueOr({});
milliseconds duration(0);
- for (const auto& effect : primitiveEffects) {
- auto durationResult = getPrimitiveDuration(effect.primitive);
- if (durationResult.isOk()) {
- duration += durationResult.value();
+ 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(primitiveEffects, cb), duration);
+
+ return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration);
}
-HalResult<milliseconds> AidlHalWrapper::getPrimitiveDuration(CompositePrimitive primitive) {
- std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
- if (mPrimitiveDurations.empty()) {
- constexpr auto primitiveRange = enum_range<CompositePrimitive>();
- constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
- mPrimitiveDurations.resize(primitiveCount);
- }
- auto primitiveIdx = static_cast<size_t>(primitive);
- if (primitiveIdx >= mPrimitiveDurations.size()) {
- // Safety check, should not happen if enum_range is correct.
- return HalResult<milliseconds>::unsupported();
- }
- auto& cache = mPrimitiveDurations[primitiveIdx];
- if (cache.has_value()) {
- return HalResult<milliseconds>::ok(*cache);
- }
- int32_t duration;
- auto result = getHal()->getPrimitiveDuration(primitive, &duration);
- if (result.isOk()) {
- // Cache copy of returned value.
- cache.emplace(duration);
- }
- return HalResult<milliseconds>::fromStatus(result, milliseconds(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() {
@@ -287,24 +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;
@@ -370,44 +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<float> HidlHalWrapper<I>::getResonantFrequency() {
- ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
- return HalResult<float>::unsupported();
-}
-
-template <typename I>
-HalResult<float> HidlHalWrapper<I>::getQFactor() {
- ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
- return HalResult<float>::unsupported();
-}
-
-template <typename I>
-HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
- const std::vector<CompositeEffect>&, const std::function<void()>&) {
- ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
- return HalResult<std::chrono::milliseconds>::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..6e660e7 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 = [](HalWrapper* hal) { return hal->ping(); };
+ return mController->doWithRetry<void>(pingFn, "ping");
}
void LegacyManagerHalWrapper::tryReconnect() {
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index f40980c..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,18 +140,18 @@
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;
}
@@ -148,22 +163,23 @@
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;
}
@@ -171,19 +187,19 @@
auto callback = []() {};
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 = 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 f884ff0..6c31e2b 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(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,53 +54,64 @@
*/
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>([](HalWrapper* hal) { return hal->getInfo(); }, sDefaultInfo, "getInfo");
+ }
- HalResult<void> setAmplitude(float 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<float> getResonantFrequency() final override;
- HalResult<float> getQFactor() final override;
-
- HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) final override;
-
- HalResult<std::chrono::milliseconds> 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.get());
+ if (result.checkAndLogFailure(functionName)) {
+ tryReconnect();
+ } else {
+ return result;
+ }
+ }
- template <typename T>
- HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+ return halFn(hal.get());
+ }
};
}; // namespace vibrator
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 667702d..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,6 +240,8 @@
*/
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;
@@ -180,25 +254,43 @@
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<float> getResonantFrequency() = 0;
- virtual HalResult<float> getQFactor() = 0;
-
virtual HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) = 0;
virtual HalResult<std::chrono::milliseconds> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) = 0;
+ 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.
@@ -230,52 +322,38 @@
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<float> getResonantFrequency() override final;
- HalResult<float> getQFactor() override final;
-
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
HalResult<std::chrono::milliseconds> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ 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;
- std::mutex mResonantFrequencyMutex;
- std::mutex mQFactorMutex;
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);
- std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
- GUARDED_BY(mSupportedPrimitivesMutex);
- std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
- std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
-
- // Loads and caches from IVibrator.
- HalResult<std::chrono::milliseconds> getPrimitiveDuration(
- hardware::vibrator::CompositePrimitive primitive);
-
- // Loads directly from IVibrator handle, skipping caches.
- HalResult<Capabilities> getCapabilitiesInternal();
- HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
-
- HalResult<float> getResonantFrequencyInternal();
- HalResult<float> getQFactorInternal();
sp<hardware::vibrator::IVibrator> getHal();
};
@@ -302,26 +380,11 @@
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<float> getResonantFrequency() override final;
- HalResult<float> getQFactor() override final;
-
- HalResult<std::chrono::milliseconds> 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/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index e438d78..8e77bc5 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,9 @@
using namespace std::chrono_literals;
using namespace testing;
-static constexpr int MAX_ATTEMPTS = 2;
+static const auto ON_FN = [](vibrator::HalWrapper* hal) { return hal->on(10ms, []() {}); };
+static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); };
+static const auto PING_FN = [](vibrator::HalWrapper* hal) { return hal->ping(); };
// -------------------------------------------------------------------------------------------------
@@ -63,21 +63,11 @@
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<float>, getResonantFrequency, (), (override));
- MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
-
MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
(Effect effect, EffectStrength strength,
const std::function<void()>& completionCallback),
(override));
- MOCK_METHOD(vibrator::HalResult<milliseconds>, 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(); }
@@ -104,64 +94,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<float> resonantFrequencyResult,
- vibrator::HalResult<float> qFactorResult,
- 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(1.0f)))
- .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(), getResonantFrequency())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(resonantFrequencyResult));
- EXPECT_CALL(*mMockHal.get(), getQFactor())
- .Times(Exactly(cardinality))
- .WillRepeatedly(Return(qFactorResult));
- 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(durationResult));
-
- if (cardinality > 1) {
- // One reconnection call after each failure.
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
- }
- }
};
// -------------------------------------------------------------------------------------------------
@@ -175,127 +107,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 = {Effect::CLICK, Effect::TICK};
- std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
- CompositePrimitive::THUD};
- constexpr float F0 = 123.f;
- constexpr float Q_FACTOR = 12.f;
- const std::vector<CompositeEffect> compositeEffects =
- {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
- 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<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
- 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(1.0f).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 getResonantFrequencyResult = mController->getResonantFrequency();
- ASSERT_TRUE(getResonantFrequencyResult.isOk());
- ASSERT_EQ(F0, getResonantFrequencyResult.value());
-
- auto getQFactorResult = mController->getQFactor();
- ASSERT_TRUE(getQFactorResult.isOk());
- ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
-
- auto performEffectResult =
- mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
- ASSERT_TRUE(performEffectResult.isOk());
- ASSERT_EQ(100ms, performEffectResult.value());
-
- auto performComposedEffectResult =
- mController->performComposedEffect(compositeEffects, []() {});
- ASSERT_TRUE(performComposedEffectResult.isOk());
- ASSERT_EQ(100ms, performComposedEffectResult.value());
+ 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<float>::unsupported(),
- vibrator::HalResult<float>::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(1.0f).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->getResonantFrequency().isUnsupported());
- ASSERT_TRUE(mController->getQFactor().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<float>::failed("message"),
- vibrator::HalResult<float>::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(1.0f).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->getResonantFrequency().isFailed());
- ASSERT_TRUE(mController->getQFactor().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);
}
@@ -312,7 +169,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);
}
@@ -325,7 +184,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(); });
@@ -341,33 +203,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(1.0f).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->getResonantFrequency().isUnsupported());
- ASSERT_TRUE(mController->getQFactor().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(15, 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);
@@ -380,14 +226,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 = [&](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 99f592f..af0cdb8 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));
@@ -289,204 +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())));
-
- 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());
- }));
- }
- 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());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
- constexpr float F0 = 123.f;
- EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
- .Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
-
- ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
- ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
-
- auto result = mWrapper->getResonantFrequency();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(F0, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
- constexpr float F0 = 123.f;
+ .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())));
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(std::thread([&]() {
- auto result = mWrapper->getResonantFrequency();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(F0, result.value());
- }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- auto result = mWrapper->getResonantFrequency();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(F0, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
- constexpr float Q_FACTOR = 123.f;
- EXPECT_CALL(*mMockHal.get(), getQFactor(_))
- .Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
-
- ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
- ASSERT_TRUE(mWrapper->getQFactor().isFailed());
-
- auto result = mWrapper->getQFactor();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(Q_FACTOR, result.value());
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
- constexpr float Q_FACTOR = 123.f;
- EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+ .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->getQFactor();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(Q_FACTOR, 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->getQFactor();
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(Q_FACTOR, 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) {
@@ -569,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));
@@ -579,24 +531,26 @@
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
-
+ .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(), compose(Eq(singleEffect), _))
- .Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
-
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
.Times(Exactly(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status())));
+
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
.Times(Exactly(1))
.WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -622,38 +576,90 @@
}
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));
- EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
- .Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
- EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
- .Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
- EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
- .Times(Exactly(3))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ {
+ 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(111ms, result.value()); // Failed primitive duration counted as 0.
+ 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(113ms, result.value()); // Second fetch succeeds and returns primitive duration.
+ 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(113ms, result.value()); // Cached durations not fetched again, same duration returned.
+ 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 33c47cf..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;
@@ -188,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([]() {
@@ -196,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) {
@@ -325,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..548d028 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 = [](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/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 866007e..eb24a22 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -108,6 +108,7 @@
cc_binary {
name: "vr_hwc",
+ enabled: false,
system_ext_specific: true,
vintf_fragments: ["manifest_vr_hwc.xml"],
srcs: [
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 020b520..8d6681c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1086,7 +1086,8 @@
ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
strerror(-err), err);
- err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+ err =
+ window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, nsecs_t{-1});
if (err != android::OK) {
ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
strerror(-err), err);
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index eb0fcc3..5872495 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -363,6 +363,10 @@
VkJsonDeviceGroup device_group;
std::vector<VkPhysicalDeviceGroupProperties> group_properties;
group_properties.resize(count);
+ for (auto& properties : group_properties) {
+ properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
+ properties.pNext = nullptr;
+ }
result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
group_properties.data());
if (result != VK_SUCCESS) {