Merge changes I70943093,Ib1974f30 into main
* changes:
tests: Eliminate annoying and noisy CompositionEngine errors
tests: Improve logging for objects that are causing errors
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index b8e5ce1..17ae0aa 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -67,6 +67,8 @@
MOCK_METHOD2(unregisterForNotifications, status_t(const String16&,
const sp<LocalRegistrationCallback>&));
MOCK_METHOD0(getServiceDebugInfo, std::vector<ServiceDebugInfo>());
+ MOCK_METHOD1(enableAddServiceCache, void(bool));
+
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 50c2cd8..36dcbca 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -50,6 +50,7 @@
"vibrator/CommandAlwaysOnEnable.cpp",
"vibrator/CommandCompose.cpp",
"vibrator/CommandComposePwle.cpp",
+ "vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp",
"vibrator/CommandGetBandwidthAmplitudeMap.cpp",
"vibrator/CommandGetCapabilities.cpp",
"vibrator/CommandGetCompositionDelayMax.cpp",
@@ -72,6 +73,11 @@
"vibrator/CommandSetExternalControl.cpp",
"vibrator/CommandSupportsAmplitudeControl.cpp",
"vibrator/CommandSupportsExternalControl.cpp",
+ "vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp",
+ "vibrator/CommandGetPwleV2CompositionSizeMax.cpp",
+ "vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp",
+ "vibrator/CommandComposePwleV2.cpp",
+ "vibrator/CommandPerformVendorEffect.cpp",
],
visibility: [":__subpackages__"],
}
diff --git a/cmds/idlcli/vibrator/CommandComposePwleV2.cpp b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
new file mode 100644
index 0000000..6d3cf84
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandComposePwleV2.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <charconv>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePwleV2;
+using aidl::PwleV2Primitive;
+
+class CommandComposePwleV2 : public Command {
+ std::string getDescription() const override { return "Compose normalized PWLE vibration."; }
+
+ std::string getUsageSummary() const override {
+ return "[options] <time> <frequency> <amplitude> ...";
+ }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
+ {"<time>", {"Segment duration in milliseconds"}},
+ {"<frequency>", {"Target frequency in Hz"}},
+ {"<amplitude>", {"Target amplitude in [0.0, 1.0]"}},
+ {"...", {"May repeat multiple times."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args& args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
+
+ if (args.empty()) {
+ std::cerr << "Missing arguments! Please see usage" << std::endl;
+ return USAGE;
+ }
+
+ while (!args.empty()) {
+ PwleV2Primitive segment;
+
+ if (auto timeMs = args.pop<decltype(segment.timeMillis)>();
+ timeMs && *timeMs >= 0 && *timeMs <= 0x7ffff) {
+ segment.timeMillis = *timeMs;
+ std::cout << "Time: " << segment.timeMillis << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Time!" << std::endl;
+ return USAGE;
+ }
+
+ if (auto frequencyHz = args.pop<decltype(segment.frequencyHz)>();
+ frequencyHz && *frequencyHz >= 30 && *frequencyHz <= 300) {
+ segment.frequencyHz = *frequencyHz;
+ std::cout << "Frequency: " << segment.frequencyHz << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Frequency!" << std::endl;
+ return USAGE;
+ }
+
+ if (auto amplitude = args.pop<decltype(segment.amplitude)>();
+ amplitude && *amplitude >= 0 && *amplitude <= 1.0) {
+ segment.amplitude = *amplitude;
+ std::cout << "Amplitude: " << segment.amplitude << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Amplitude!" << std::endl;
+ return USAGE;
+ }
+
+ mCompositePwle.pwlePrimitives.emplace_back(std::move(segment));
+ }
+
+ 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::composePwleV2, mCompositePwle, callback);
+
+ if (status.isOk() && callback) {
+ callback->waitForComplete();
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
+ }
+
+ bool mBlocking;
+ CompositePwleV2 mCompositePwle;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandComposePwleV2>("composePwleV2");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
new file mode 100644
index 0000000..2edd0ca
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetFrequencyToOutputAccelerationMap.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::FrequencyAccelerationMapEntry;
+
+class CommandGetFrequencyToOutputAccelerationMap : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator frequency to output acceleration 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<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getFrequencyToOutputAccelerationMap,
+ &frequencyToOutputAccelerationMap);
+ statusStr = status.getDescription();
+ ret = (status.isOk() ? OK : ERROR);
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Frequency to Output Amplitude Map: " << std::endl;
+ for (auto& entry : frequencyToOutputAccelerationMap) {
+ std::cout << entry.frequencyHz << " " << entry.maxOutputAccelerationGs << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyToOutputAccelerationMap>(
+ "getFrequencyToOutputAccelerationMap");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
new file mode 100644
index 0000000..cca072c
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2CompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleV2CompositionSizeMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator PwleV2 max composition size.";
+ }
+
+ 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::getPwleV2CompositionSizeMax, &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<CommandGetPwleV2CompositionSizeMax>(
+ "getPwleV2CompositionSizeMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
new file mode 100644
index 0000000..dbbfe1a
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMaxMillis.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleV2PrimitiveDurationMaxMillis : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator PwleV2 max primitive duration 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::getPwleV2PrimitiveDurationMaxMillis,
+ &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<CommandGetPwleV2PrimitiveDurationMaxMillis>(
+ "getPwleV2PrimitiveDurationMaxMillis");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
new file mode 100644
index 0000000..09225c4
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPwleV2PrimitiveDurationMinMillis.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetPwleV2PrimitiveDurationMinMillis : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator PwleV2 minimum primitive duration 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 minDurationMs;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPwleV2PrimitiveDurationMinMillis,
+ &minDurationMs);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Primitive duration min: " << minDurationMs << " ms" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetPwleV2PrimitiveDurationMinMillis>(
+ "getPwleV2PrimitiveDurationMinMillis");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandPerformVendorEffect.cpp b/cmds/idlcli/vibrator/CommandPerformVendorEffect.cpp
new file mode 100644
index 0000000..9c25bd2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandPerformVendorEffect.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <thread>
+
+#include "utils.h"
+#include "vibrator.h"
+
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::VendorEffect;
+
+class CommandPerformVendorEffect : public Command {
+ std::string getDescription() const override { return "Perform vendor vibration effect."; }
+
+ std::string getUsageSummary() const override { return "[options] <none>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
+ {"<none>", {"No valid input."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args& args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
+
+ return OK;
+ }
+
+ Status doMain(Args&& /*args*/) override { return UNAVAILABLE; }
+
+ bool mBlocking;
+ VendorEffect mEffect;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandPerformVendorEffect>(
+ "performVendorEffect");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index fa7cb64..38a125b 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -396,7 +396,7 @@
SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
- *outBinder = tryGetBinder(name, true);
+ *outBinder = tryGetBinder(name, true).service;
// returns ok regardless of result for legacy reasons
return Status::ok();
}
@@ -430,13 +430,15 @@
return os::Service::make<os::Service::Tag::accessor>(nullptr);
}
return os::Service::make<os::Service::Tag::accessor>(
- tryGetBinder(*accessorName, startIfNotFound));
+ tryGetBinder(*accessorName, startIfNotFound).service);
} else {
- return os::Service::make<os::Service::Tag::binder>(tryGetBinder(name, startIfNotFound));
+ return os::Service::make<os::Service::Tag::serviceWithMetadata>(
+ tryGetBinder(name, startIfNotFound));
}
}
-sp<IBinder> ServiceManager::tryGetBinder(const std::string& name, bool startIfNotFound) {
+os::ServiceWithMetadata ServiceManager::tryGetBinder(const std::string& name,
+ bool startIfNotFound) {
SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
@@ -450,13 +452,13 @@
if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name
<< "', but the service is not allowed for isolated apps.";
- return nullptr;
+ return os::ServiceWithMetadata();
}
out = service->binder;
}
if (!mAccess->canFind(ctx, name)) {
- return nullptr;
+ return os::ServiceWithMetadata();
}
if (!out && startIfNotFound) {
@@ -473,8 +475,11 @@
CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false));
service->guaranteeClient = true;
}
-
- return out;
+ os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
+ serviceWithMetadata.service = out;
+ serviceWithMetadata.isLazyService =
+ service ? service->dumpPriority & FLAG_IS_LAZY_SERVICE : false;
+ return serviceWithMetadata;
}
bool isValidServiceName(const std::string& name) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index c92141b..964abee 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -114,7 +114,7 @@
void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
os::Service tryGetService(const std::string& name, bool startIfNotFound);
- sp<IBinder> tryGetBinder(const std::string& name, bool startIfNotFound);
+ os::ServiceWithMetadata tryGetBinder(const std::string& name, bool startIfNotFound);
binder::Status canAddService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor);
binder::Status canFindService(const Access::CallingContext& ctx, const std::string& name,
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 95f459f..e620770 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -92,6 +92,11 @@
auto sm = getPermissiveServiceManager();
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ EXPECT_TRUE(sm->addService("lazyfoo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT |
+ IServiceManager::FLAG_IS_LAZY_SERVICE)
+ .isOk());
}
TEST(AddService, EmptyNameDisallowed) {
@@ -156,7 +161,7 @@
Service outA;
EXPECT_TRUE(sm->getService2("foo", &outA).isOk());
- EXPECT_EQ(serviceA, outA.get<Service::Tag::binder>());
+ EXPECT_EQ(serviceA, outA.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinderA;
EXPECT_TRUE(sm->getService("foo", &outBinderA).isOk());
EXPECT_EQ(serviceA, outBinderA);
@@ -168,7 +173,7 @@
Service outB;
EXPECT_TRUE(sm->getService2("foo", &outB).isOk());
- EXPECT_EQ(serviceB, outB.get<Service::Tag::binder>());
+ EXPECT_EQ(serviceB, outB.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinderB;
EXPECT_TRUE(sm->getService("foo", &outBinderB).isOk());
EXPECT_EQ(serviceB, outBinderB);
@@ -195,7 +200,7 @@
Service out;
EXPECT_TRUE(sm->getService2("foo", &out).isOk());
- EXPECT_EQ(service, out.get<Service::Tag::binder>());
+ EXPECT_EQ(service, out.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinder;
EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
EXPECT_EQ(service, outBinder);
@@ -206,7 +211,7 @@
Service out;
EXPECT_TRUE(sm->getService2("foo", &out).isOk());
- EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ EXPECT_EQ(nullptr, out.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinder;
EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
EXPECT_EQ(nullptr, outBinder);
@@ -227,7 +232,7 @@
Service out;
// returns nullptr but has OK status for legacy compatibility
EXPECT_TRUE(sm->getService2("foo", &out).isOk());
- EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ EXPECT_EQ(nullptr, out.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinder;
EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
EXPECT_EQ(nullptr, outBinder);
@@ -257,7 +262,7 @@
Service out;
EXPECT_TRUE(sm->getService2("foo", &out).isOk());
- EXPECT_EQ(service, out.get<Service::Tag::binder>());
+ EXPECT_EQ(service, out.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinder;
EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
EXPECT_EQ(service, outBinder);
@@ -289,7 +294,7 @@
Service out;
// returns nullptr but has OK status for legacy compatibility
EXPECT_TRUE(sm->getService2("foo", &out).isOk());
- EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ EXPECT_EQ(nullptr, out.get<Service::Tag::serviceWithMetadata>().service);
sp<IBinder> outBinder;
EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
EXPECT_EQ(nullptr, outBinder);
diff --git a/include/android/OWNERS b/include/android/OWNERS
index 5b014d0..9dfa279 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -9,4 +9,5 @@
# ADPF
per-file performance_hint.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file system_health.h = file:platform/frameworks/base:/ADPF_OWNERS
per-file thermal.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index ca86c27..2b4a5f5 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -359,6 +359,31 @@
bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
/**
+ * Informs the framework of an upcoming one-off expensive frame for a graphics pipeline
+ * bound to this session. This frame will be treated as not representative of the workload as a
+ * whole, and it will be discarded the purposes of load tracking. The user can specify
+ * whether the workload spike is expected to be on the CPU, GPU, or both.
+ *
+ * Sending hints for both CPU and GPU counts as two separate hints for the purposes of the
+ * rate limiter.
+ *
+ * @param cpu Indicates if the workload spike is expected to affect the CPU.
+ * @param gpu Indicates if the workload spike is expected to affect the GPU.
+ * @param debugName A required string used to identify this specific hint during
+ * tracing. This debug string will only be held for the duration of the
+ * method, and can be safely discarded after.
+ *
+ * @return 0 on success.
+ * EINVAL if no hints were requested.
+ * EBUSY if the hint was rate limited.
+ * EPIPE if communication with the system service has failed.
+ * ENOTSUP if the hint is not supported.
+ */
+int APerformanceHint_notifyWorkloadSpike(
+ APerformanceHintSession* _Nonnull session,
+ bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+
+/**
* Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
* instances managed by this session.
*
diff --git a/include/android/system_health.h b/include/android/system_health.h
new file mode 100644
index 0000000..69620df
--- /dev/null
+++ b/include/android/system_health.h
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @defgroup SystemHealth
+* @{
+*/
+
+/**
+ * @file system_health.h
+ */
+
+#ifndef _ANDROID_SYSTEM_HEALTH_H
+#define _ANDROID_SYSTEM_HEALTH_H
+
+#include <sys/cdefs.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit).
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Params used to customize the calculation of CPU headroom.
+ *
+ * Also see {@link ASystemHealth_getCpuHeadroom}.
+ */
+typedef struct ACpuHeadroomParams ACpuHeadroomParams;
+
+/**
+ * Params used to customize the calculation of GPU headroom.
+ *
+ * Also see {@link ASystemHealth_getGpuHeadroom}.
+ */
+typedef struct AGpuHeadroomParams AGpuHeadroomParams;
+
+/**
+ * Creates a new instance of ACpuHeadroomParams.
+ *
+ * When the client finishes using {@link ACpuHeadroomParams},
+ * {@link ACpuHeadroomParams_destroy()} must be called to destroy
+ * and free up the resources associated with {@link ACpuHeadroomParams}.
+ *
+ * Available since API level 36.
+ *
+ * @return A new instance of ACpuHeadroomParams.
+ */
+ACpuHeadroomParams *_Nonnull ACpuHeadroomParams_create()
+__INTRODUCED_IN(36);
+
+enum ACpuHeadroomCalculationType {
+ /**
+ * Use the minimum headroom value within the calculation window.
+ * Introduced in API level 36.
+ */
+ ACPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
+ /**
+ * Use the average headroom value within the calculation window.
+ * Introduced in API level 36.
+ */
+ ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
+};
+typedef enum ACpuHeadroomCalculationType ACpuHeadroomCalculationType;
+
+enum AGpuHeadroomCalculationType {
+ /**
+ * Use the minimum headroom value within the calculation window.
+ * Introduced in API level 36.
+ */
+ AGPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
+ /**
+ * Use the average headroom value within the calculation window.
+ * Introduced in API level 36.
+ */
+ AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
+};
+typedef enum AGpuHeadroomCalculationType AGpuHeadroomCalculationType;
+
+/**
+ * Sets the headroom calculation window size in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ * window size, the larger fluctuation in the headroom value should be expected.
+ * The default value can be retrieved from the
+ * {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device
+ * will try to use the closest feasible window size to this param.
+ */
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params,
+ int windowMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation window size in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return This will return the default value chosen by the device if the params is not set.
+ */
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the headroom calculation window size in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ * window size, the larger fluctuation in the headroom value should be expected.
+ * The default value can be retrieved from the
+ * {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device
+ * will try to use the closest feasible window size to this param.
+ */
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params,
+ int windowMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation window size in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return This will return the default value chosen by the device if the params is not set.
+ */
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the headroom calculation type in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param calculationType The headroom calculation type.
+ */
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams *_Nonnull params,
+ ACpuHeadroomCalculationType calculationType)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation type in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return The headroom calculation type.
+ */
+ACpuHeadroomCalculationType
+ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the headroom calculation type in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param calculationType The headroom calculation type.
+ */
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams *_Nonnull params,
+ AGpuHeadroomCalculationType calculationType)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the headroom calculation type in AGpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @return The headroom calculation type.
+ */
+AGpuHeadroomCalculationType
+AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Sets the thread TIDs to track in ACpuHeadroomParams.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be set.
+ * @param tids Non-null array of TIDs, maximum 5.
+ * @param tidsSize The size of the tids array.
+ */
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams *_Nonnull params, const int *_Nonnull tids,
+ int tidsSize)
+__INTRODUCED_IN(36);
+
+/**
+ * Creates a new instance of AGpuHeadroomParams.
+ *
+ * When the client finishes using {@link AGpuHeadroomParams},
+ * {@link AGpuHeadroomParams_destroy()} must be called to destroy
+ * and free up the resources associated with {@link AGpuHeadroomParams}.
+ *
+ * Available since API level 36.
+ *
+ * @return A new instance of AGpuHeadroomParams.
+ */
+AGpuHeadroomParams *_Nonnull AGpuHeadroomParams_create()
+__INTRODUCED_IN(36);
+
+/**
+ * Deletes the ACpuHeadroomParams instance.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be deleted.
+ */
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Deletes the AGpuHeadroomParams instance.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to be deleted.
+ */
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams *_Nonnull params)
+__INTRODUCED_IN(36);
+
+/**
+ * Provides an estimate of available CPU capacity headroom of the device.
+ *
+ * The value can be used by the calling application to determine if the workload was CPU bound and
+ * then take action accordingly to ensure that the workload can be completed smoothly. It can also
+ * be used with the thermal status and headroom to determine if reducing the CPU bound workload can
+ * help reduce the device temperature to avoid thermal throttling.
+ *
+ * Available since API level 36.
+ *
+ * @param params The params to customize the CPU headroom calculation, or nullptr to use the default
+ * @param outHeadroom Non-null output pointer to a single float, which will be set to the CPU
+ * headroom value. The value will be a single value or `Float.NaN` if it's
+ * temporarily unavailable.
+ * Each valid value ranges from [0, 100], where 0 indicates no more cpu resources
+ * can be granted.
+ * @return 0 on success
+ * EPIPE if failed to get the CPU headroom.
+ * EPERM if the TIDs do not belong to the same process.
+ * ENOTSUP if API or requested params is unsupported.
+ */
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams *_Nullable params,
+ float *_Nonnull outHeadroom)
+__INTRODUCED_IN(36);
+
+/**
+ * Provides an estimate of available GPU capacity headroom of the device.
+ *
+ * The value can be used by the calling application to determine if the workload was GPU bound and
+ * then take action accordingly to ensure that the workload can be completed smoothly. It can also
+ * be used with the thermal status and headroom to determine if reducing the GPU bound workload can
+ * help reduce the device temperature to avoid thermal throttling.
+ *
+ * Available since API level 36
+ *
+ * @param params The params to customize the GPU headroom calculation, or nullptr to use the default
+ * @param outHeadroom Non-null output pointer to a single float, which will be set to the GPU
+ * headroom value. The value will be a single value or `Float.NaN` if it's
+ * temporarily unavailable.
+ * Each valid value ranges from [0, 100], where 0 indicates no more gpu resources
+ * can be granted.
+ * @return 0 on success
+ * EPIPE if failed to get the GPU headroom.
+ * ENOTSUP if API or requested params is unsupported.
+ */
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams *_Nullable params,
+ float *_Nonnull outHeadroom)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets minimum polling interval for calling {@link ASystemHealth_getCpuHeadroom} in milliseconds.
+ *
+ * The getCpuHeadroom API may return cached result if called more frequently than the interval.
+ *
+ * Available since API level 36.
+ *
+ * @param outMinIntervalMillis Non-null output pointer to a int64_t, which
+ * will be set to the minimum polling interval in milliseconds.
+ * @return 0 on success
+ * EPIPE if failed to get the minimum polling interval.
+ * ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets minimum polling interval for calling {@link ASystemHealth_getGpuHeadroom} in milliseconds.
+ *
+ * The getGpuHeadroom API may return cached result if called more frequent than the interval.
+ *
+ * Available since API level 36.
+ *
+ * @param outMinIntervalMillis Non-null output pointer to a int64_t, which
+ * will be set to the minimum polling interval in milliseconds.
+ * @return 0 on success
+ * EPIPE if failed to get the minimum polling interval.
+ * ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+__INTRODUCED_IN(36);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _ANDROID_SYSTEM_HEALTH_H
+
+/** @} */
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 7f9d2ed..e5d46b5 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -140,45 +140,47 @@
* Available since API level 30.
*
* @param manager The manager instance to use to query the thermal status.
- * Acquired via {@link AThermal_acquireManager}.
+ * Acquired via {@link AThermal_acquireManager}.
*
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
*/
AThermalStatus
-AThermal_getCurrentThermalStatus(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30);
+AThermal_getCurrentThermalStatus(AThermalManager *_Nonnull manager) __INTRODUCED_IN(30);
/**
- * Register the thermal status listener for thermal status change.
+ * Register a thermal status listener for thermal status change.
*
* Available since API level 30.
*
* @param manager The manager instance to use to register.
- * Acquired via {@link AThermal_acquireManager}.
- * @param callback The callback function to be called when thermal status updated.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called on system binder thread pool when thermal
+ * status updated.
* @param data The data pointer to be passed when callback is called.
*
* @return 0 on success
* EINVAL if the listener and data pointer were previously added and not removed.
- * EPERM if the required permission is not held.
- * EPIPE if communication with the system service has failed.
+ * EPIPE if communication with the system service has failed, the listener will not get
+ * removed and this call should be retried
*/
-int AThermal_registerThermalStatusListener(AThermalManager* _Nonnull manager,
+int AThermal_registerThermalStatusListener(AThermalManager *_Nonnull manager,
AThermal_StatusCallback _Nullable callback,
void* _Nullable data) __INTRODUCED_IN(30);
/**
- * Unregister the thermal status listener previously resgistered.
+ * Unregister a thermal status listener previously registered.
+ *
+ * No subsequent invocations of the callback will occur after this function returns successfully.
*
* Available since API level 30.
*
* @param manager The manager instance to use to unregister.
- * Acquired via {@link AThermal_acquireManager}.
- * @param callback The callback function to be called when thermal status updated.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function that was previously registered.
* @param data The data pointer to be passed when callback is called.
*
* @return 0 on success
* EINVAL if the listener and data pointer were not previously added.
- * EPERM if the required permission is not held.
* EPIPE if communication with the system service has failed.
*/
int AThermal_unregisterThermalStatusListener(AThermalManager* _Nonnull manager,
@@ -254,7 +256,7 @@
* The headroom threshold is used to interpret the possible thermal throttling status based on
* the headroom prediction. For example, if the headroom threshold for
* {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
- * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system
+ * (or `AThermal_getThermalHeadroom(10)=0.75}`, one can expect that in 10 seconds the system
* could be in lightly throttled state if the workload remains the same. The app can consider
* taking actions according to the nearest throttling status the difference between the headroom and
* the threshold.
@@ -262,24 +264,30 @@
* For new devices it's guaranteed to have a single sensor, but for older devices with multiple
* sensors reporting different threshold values, the minimum threshold is taken to be conservative
* on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value
- * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above
+ * of 0.75 (or `AThermal_getThermalHeadroom(0)=0.75`) exceeding the threshold of 0.7 above
* will always come with lightly throttled state
- * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower
- * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}).
+ * (or `AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT`) but it can be lower
+ * (or `AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE`).
* While it's always guaranteed that the device won't be throttled heavier than the unmet
* threshold's state, so a real-time headroom of 0.75 will never come with
* {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with
* {@link ATHERMAL_STATUS_LIGHT} but {@link #ATHERMAL_STATUS_NONE}.
* <p>
- * The returned list of thresholds is cached on first successful query and owned by the thermal
- * manager, which will not change between calls to this function. The caller should only need to
- * free the manager with {@link AThermal_releaseManager}.
+ * Starting in Android 16, this polling API may return different results when called depending on
+ * the device. The new headroom listener API {@link #AThermal_HeadroomCallback} can be used to
+ * detect headroom thresholds changes.
+ * <p>
+ * Before API level 36 the returned list of thresholds is cached on first successful query and owned
+ * by the thermal manager, which will not change between calls to this function. The caller should
+ * only need to free the manager with {@link AThermal_releaseManager}.
+ * <p>
*
* @param manager The manager instance to use.
* Acquired via {@link AThermal_acquireManager}.
* @param outThresholds non-null output pointer to null AThermalHeadroomThreshold pointer, which
- * will be set to the cached array of thresholds if thermal thresholds are supported
- * by the system or device, otherwise nullptr or unmodified.
+ * will be set to a new array of thresholds if thermal thresholds are supported
+ * by the system or device, otherwise nullptr or unmodified. The client should
+ * clean up the thresholds by array-deleting the threshold pointer.
* @param size non-null output pointer whose value will be set to the size of the threshold array
* or 0 if it's not supported.
* @return 0 on success
@@ -292,6 +300,71 @@
* _Nullable outThresholds,
size_t* _Nonnull size) __INTRODUCED_IN(35);
+/**
+ * Prototype of the function that is called when thermal headroom or thresholds changes.
+ * It's passed the updated thermal headroom and thresholds as parameters, as well as the
+ * pointer provided by the client that registered a callback.
+ *
+ * @param data The data pointer to be passed when callback is called.
+ * @param headroom The current non-negative normalized headroom value, also see
+ * {@link AThermal_getThermalHeadroom}.
+ * @param forecastHeadroom The forecasted non-negative normalized headroom value, also see
+ * {@link AThermal_getThermalHeadroom}.
+ * @param forecastSeconds The seconds used for the forecast by the system.
+ * @param thresholds The current headroom thresholds. The thresholds pointer will be a constant
+ * shared across all callbacks registered from the same process, and it will be
+ * destroyed after all the callbacks are finished. If the client intents to
+ * persist the values, it should make a copy of it during the callback.
+ * @param thresholdsCount The count of thresholds.
+ */
+typedef void (*AThermal_HeadroomCallback)(void *_Nullable data,
+ float headroom,
+ float forecastHeadroom,
+ int forecastSeconds,
+ const AThermalHeadroomThreshold* _Nullable thresholds,
+ size_t thresholdsCount);
+
+/**
+ * Register a thermal headroom listener for thermal headroom or thresholds change.
+ *
+ * Available since API level 36.
+ *
+ * @param manager The manager instance to use to register.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called on system binder thread pool when thermal
+ * headroom or thresholds update.
+ * @param data The data pointer to be passed when callback is called.
+ *
+ * @return 0 on success
+ * EINVAL if the listener and data pointer were previously added and not removed.
+ * EPIPE if communication with the system service has failed.
+ */
+int AThermal_registerThermalHeadroomListener(AThermalManager* _Nonnull manager,
+ AThermal_HeadroomCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(36);
+
+/**
+ * Unregister a thermal headroom listener previously registered.
+ *
+ * No subsequent invocations of the callback will occur after this function returns successfully.
+ *
+ * Available since API level 36.
+ *
+ * @param manager The manager instance to use to unregister.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function that was previously registered.
+ * @param data The data pointer that was previously registered.
+ *
+ * @return 0 on success
+ * EINVAL if the listener and data pointer were not previously added.
+ * EPIPE if communication with the system service has failed, the listener will not get
+ * removed and this call should be retried
+ */
+
+int AThermal_unregisterThermalHeadroomListener(AThermalManager* _Nonnull manager,
+ AThermal_HeadroomCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(36);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h
new file mode 100644
index 0000000..90427bd
--- /dev/null
+++ b/include/input/DisplayTopologyGraph.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/enum.h>
+#include <ui/LogicalDisplayId.h>
+
+#include <cinttypes>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+/**
+ * The edge of the current display, where adjacent display is attached to.
+ */
+enum class DisplayTopologyPosition : int32_t {
+ LEFT = 0,
+ TOP = 1,
+ RIGHT = 2,
+ BOTTOM = 3,
+
+ ftl_last = BOTTOM
+};
+
+/**
+ * Directed edge in the graph of adjacent displays.
+ */
+struct DisplayTopologyAdjacentDisplay {
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
+ DisplayTopologyPosition position;
+ float offsetPx;
+};
+
+/**
+ * Directed Graph representation of Display Topology.
+ */
+struct DisplayTopologyGraph {
+ ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId::INVALID;
+ std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph;
+};
+
+} // namespace android
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index f150fb1..e3f98ba 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -74,6 +74,20 @@
* baseline to prepare for an arbitrary load, and must wake up if inactive.
*/
GPU_LOAD_RESET = 7,
+
+ /**
+ * This hint indicates an upcoming CPU workload that is abnormally large and
+ * not representative of the workload. This should be used for rare, one-time
+ * operations and should be ignored by any load tracking or session hysteresis.
+ */
+ CPU_LOAD_SPIKE = 8,
+
+ /**
+ * This hint indicates an upcoming GPU workload that is abnormally large and
+ * not representative of the workload. This should be used for rare, one-time
+ * operations and should be ignored by any load tracking or session hysteresis.
+ */
+ GPU_LOAD_SPIKE = 9,
};
// Allows access to PowerHAL's SessionTags without needing to import its AIDL
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 0a61178..aac369d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -456,6 +456,28 @@
}
soong_config_module_type {
+ name: "libbinder_remove_cache_static_list_config",
+ module_type: "cc_defaults",
+ config_namespace: "libbinder",
+ bool_variables: ["release_libbinder_remove_cache_static_list"],
+ properties: [
+ "cflags",
+ ],
+}
+
+libbinder_remove_cache_static_list_config {
+ name: "libbinder_remove_cache_static_list_flag",
+ soong_config_variables: {
+ release_libbinder_remove_cache_static_list: {
+ cflags: ["-DLIBBINDER_REMOVE_CACHE_STATIC_LIST"],
+ conditions_default: {
+ cflags: ["-DNO_LIBBINDER_REMOVE_CACHE_STATIC_LIST"],
+ },
+ },
+ },
+}
+
+soong_config_module_type {
name: "libbinder_client_cache_config",
module_type: "cc_defaults",
config_namespace: "libbinder",
@@ -477,9 +499,35 @@
},
}
+soong_config_module_type {
+ name: "libbinder_addservice_cache_config",
+ module_type: "cc_defaults",
+ config_namespace: "libbinder",
+ bool_variables: ["release_libbinder_addservice_cache"],
+ properties: [
+ "cflags",
+ ],
+}
+
+libbinder_addservice_cache_config {
+ name: "libbinder_addservice_cache_flag",
+ soong_config_variables: {
+ release_libbinder_addservice_cache: {
+ cflags: ["-DLIBBINDER_ADDSERVICE_CACHE"],
+ conditions_default: {
+ cflags: ["-DNO_LIBBINDER_ADDSERVICE_CACHE"],
+ },
+ },
+ },
+}
+
cc_defaults {
name: "libbinder_kernel_defaults",
- defaults: ["libbinder_client_cache_flag"],
+ defaults: [
+ "libbinder_client_cache_flag",
+ "libbinder_addservice_cache_flag",
+ "libbinder_remove_cache_static_list_flag",
+ ],
srcs: [
"BufferedTextOutput.cpp",
"BackendUnifiedServiceManager.cpp",
@@ -801,6 +849,7 @@
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
"aidl/android/os/Service.aidl",
+ "aidl/android/os/ServiceWithMetadata.aidl",
"aidl/android/os/ServiceDebugInfo.aidl",
],
path: "aidl",
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index d32eecd..03d687a 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -16,6 +16,7 @@
#include "BackendUnifiedServiceManager.h"
#include <android/os/IAccessor.h>
+#include <android/os/IServiceManager.h>
#include <binder/RpcSession.h>
#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__)
@@ -30,6 +31,18 @@
constexpr bool kUseCache = false;
#endif
+#ifdef LIBBINDER_ADDSERVICE_CACHE
+constexpr bool kUseCacheInAddService = true;
+#else
+constexpr bool kUseCacheInAddService = false;
+#endif
+
+#ifdef LIBBINDER_REMOVE_CACHE_STATIC_LIST
+constexpr bool kRemoveStaticList = true;
+#else
+constexpr bool kRemoveStaticList = false;
+#endif
+
using AidlServiceManager = android::os::IServiceManager;
using android::os::IAccessor;
using binder::Status;
@@ -104,6 +117,13 @@
// go/keep-sorted end
};
+os::ServiceWithMetadata createServiceWithMetadata(const sp<IBinder>& service, bool isLazyService) {
+ os::ServiceWithMetadata out = os::ServiceWithMetadata();
+ out.service = service;
+ out.isLazyService = isLazyService;
+ return out;
+}
+
bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
sp<ProcessState> self = ProcessState::selfOrNull();
if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
@@ -125,31 +145,44 @@
if (!kUseCache) {
return Status::ok();
}
+
+ if (service.getTag() == os::Service::Tag::serviceWithMetadata) {
+ auto serviceWithMetadata = service.get<os::Service::Tag::serviceWithMetadata>();
+ return updateCache(serviceName, serviceWithMetadata.service,
+ serviceWithMetadata.isLazyService);
+ }
+ return Status::ok();
+}
+
+Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
+ const sp<IBinder>& binder, bool isServiceLazy) {
std::string traceStr;
+ // Don't cache if service is lazy
+ if (kRemoveStaticList && isServiceLazy) {
+ return Status::ok();
+ }
if (atrace_is_tag_enabled(ATRACE_TAG_AIDL)) {
traceStr = "BinderCacheWithInvalidation::updateCache : " + serviceName;
}
binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, traceStr.c_str());
-
- if (service.getTag() == os::Service::Tag::binder) {
- sp<IBinder> binder = service.get<os::Service::Tag::binder>();
- if (!binder) {
- binder::ScopedTrace
- aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache failed: binder_null");
- } else if (!binder->isBinderAlive()) {
- binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache failed: "
- "isBinderAlive_false");
- } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
- binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache successful");
- return mCacheForGetService->setItem(serviceName, binder);
- } else {
- binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache failed: "
- "caching_not_enabled");
- }
+ if (!binder) {
+ binder::ScopedTrace
+ aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: binder_null");
+ } else if (!binder->isBinderAlive()) {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: "
+ "isBinderAlive_false");
+ }
+ // If we reach here with kRemoveStaticList=true then we know service isn't lazy
+ else if (kRemoveStaticList || mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache successful");
+ return mCacheForGetService->setItem(serviceName, binder);
+ } else {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: "
+ "caching_not_enabled");
}
return Status::ok();
}
@@ -162,7 +195,7 @@
sp<IBinder> item = mCacheForGetService->getItem(serviceName);
// TODO(b/363177618): Enable caching for binders which are always null.
if (item != nullptr && item->isBinderAlive()) {
- *_out = os::Service::make<os::Service::Tag::binder>(item);
+ *_out = createServiceWithMetadata(item, false);
return true;
}
return false;
@@ -177,7 +210,7 @@
sp<IBinder>* _aidl_return) {
os::Service service;
Status status = getService2(name, &service);
- *_aidl_return = service.get<os::Service::Tag::binder>();
+ *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
return status;
}
@@ -216,14 +249,16 @@
Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
const os::Service& in, os::Service* _out) {
switch (in.getTag()) {
- case os::Service::Tag::binder: {
- if (in.get<os::Service::Tag::binder>() == nullptr) {
+ case os::Service::Tag::serviceWithMetadata: {
+ auto serviceWithMetadata = in.get<os::Service::Tag::serviceWithMetadata>();
+ if (serviceWithMetadata.service == nullptr) {
// failed to find a service. Check to see if we have any local
// injected Accessors for this service.
os::Service accessor;
Status status = getInjectedAccessor(name, &accessor);
if (!status.isOk()) {
- *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
+ *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(
+ createServiceWithMetadata(nullptr, false));
return status;
}
if (accessor.getTag() == os::Service::Tag::accessor &&
@@ -244,7 +279,8 @@
sp<IAccessor> accessor = interface_cast<IAccessor>(accessorBinder);
if (accessor == nullptr) {
ALOGE("Service#accessor doesn't have accessor. VM is maybe starting...");
- *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
+ *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(
+ createServiceWithMetadata(nullptr, false));
return Status::ok();
}
auto request = [=] {
@@ -265,7 +301,8 @@
return Status::fromStatusT(status);
}
session->setSessionSpecificRoot(accessorBinder);
- *_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject());
+ *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(
+ createServiceWithMetadata(session->getRootObject(), false));
return Status::ok();
}
default: {
@@ -277,7 +314,13 @@
Status BackendUnifiedServiceManager::addService(const ::std::string& name,
const sp<IBinder>& service, bool allowIsolated,
int32_t dumpPriority) {
- return mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
+ Status status = mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
+ // mEnableAddServiceCache is true by default.
+ if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
+ return updateCache(name, service,
+ dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
+ }
+ return status;
}
Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority,
::std::vector<::std::string>* _aidl_return) {
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index abc0eda..6a0d06a 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -105,8 +105,7 @@
binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
"BinderCacheWithInvalidation::setItem Successfully Cached");
std::lock_guard<std::mutex> lock(mCacheMutex);
- Entry entry = {.service = item, .deathRecipient = deathRecipient};
- mCache[key] = entry;
+ mCache[key] = {.service = item, .deathRecipient = deathRecipient};
return binder::Status::ok();
}
@@ -147,17 +146,21 @@
const sp<IBinder>& service) override;
binder::Status getServiceDebugInfo(::std::vector<os::ServiceDebugInfo>* _aidl_return) override;
+ void enableAddServiceCache(bool value) { mEnableAddServiceCache = value; }
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
return mTheRealServiceManager->getInterfaceDescriptor();
}
private:
+ bool mEnableAddServiceCache = true;
std::shared_ptr<BinderCacheWithInvalidation> mCacheForGetService;
sp<os::IServiceManager> mTheRealServiceManager;
binder::Status toBinderService(const ::std::string& name, const os::Service& in,
os::Service* _out);
binder::Status updateCache(const std::string& serviceName, const os::Service& service);
+ binder::Status updateCache(const std::string& serviceName, const sp<IBinder>& binder,
+ bool isLazyService);
bool returnIfCached(const std::string& serviceName, os::Service* _out);
};
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 39d8c24..53435c3 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -127,6 +127,8 @@
}
IBinder* onAsBinder() override { return IInterface::asBinder(mUnifiedServiceManager).get(); }
+ void enableAddServiceCache(bool value) { mUnifiedServiceManager->enableAddServiceCache(value); }
+
protected:
sp<BackendUnifiedServiceManager> mUnifiedServiceManager;
// AidlRegistrationCallback -> services that its been registered for
@@ -150,7 +152,8 @@
virtual Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
Service service;
Status status = mUnifiedServiceManager->getService2(name, &service);
- *_aidl_return = service.get<Service::Tag::binder>();
+ auto serviceWithMetadata = service.get<Service::Tag::serviceWithMetadata>();
+ *_aidl_return = serviceWithMetadata.service;
return status;
}
};
@@ -605,7 +608,7 @@
if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
return nullptr;
}
- return ret.get<Service::Tag::binder>();
+ return ret.get<Service::Tag::serviceWithMetadata>().service;
}
status_t CppBackendShim::addService(const String16& name, const sp<IBinder>& service,
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 0f0af0b..27cfe0c 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -19,7 +19,6 @@
#include <android/os/BnClientCallback.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
#include <binder/LazyServiceRegistrar.h>
namespace android {
@@ -134,6 +133,12 @@
std::string regStr = (reRegister) ? "Re-registering" : "Registering";
ALOGI("%s service %s", regStr.c_str(), name.c_str());
+ if (dumpFlags & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE) {
+ ALOGW("FLAG_IS_LAZY_SERVICE flag already set. This should only be set by "
+ "ClientCounterCallbackImpl in LazyServiceRegistrar");
+ }
+ dumpFlags |= android::os::IServiceManager::FLAG_IS_LAZY_SERVICE;
+
if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags);
!status.isOk()) {
ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str());
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 99a9c91..9e5e79f 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -70,6 +70,9 @@
"name": "CtsOsTestCases_ParcelAndBinderTests"
},
{
+ "name": "FrameworksCoreTests_all_binder"
+ },
+ {
"name": "libbinder_rs-internal_test"
},
{
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 1d1f84f..69edef8 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -49,6 +49,8 @@
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
| DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT;
+ const int FLAG_IS_LAZY_SERVICE = 1 << 30;
+
/* Allows services to dump sections in protobuf format. */
const int DUMP_FLAG_PROTO = 1 << 4;
@@ -61,7 +63,8 @@
*
* Returns null if the service does not exist.
*
- * @deprecated TODO(b/355394904): Use getService2 instead.
+ * @deprecated TODO(b/355394904): Use getService2 instead. This does not return metadata
+ * that is included in ServiceWithMetadata
*/
@UnsupportedAppUsage
@nullable IBinder getService(@utf8InCpp String name);
diff --git a/libs/binder/aidl/android/os/Service.aidl b/libs/binder/aidl/android/os/Service.aidl
index 4c52109..3bc6588 100644
--- a/libs/binder/aidl/android/os/Service.aidl
+++ b/libs/binder/aidl/android/os/Service.aidl
@@ -16,6 +16,8 @@
package android.os;
+import android.os.ServiceWithMetadata;
+
/**
* Service is a union of different service types that can be returned
* by the internal {@link ServiceManager#getService(name)} API.
@@ -23,6 +25,6 @@
* @hide
*/
union Service {
- @nullable IBinder binder;
+ ServiceWithMetadata serviceWithMetadata;
@nullable IBinder accessor;
}
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/ServiceWithMetadata.aidl b/libs/binder/aidl/android/os/ServiceWithMetadata.aidl
new file mode 100644
index 0000000..96f76ff
--- /dev/null
+++ b/libs/binder/aidl/android/os/ServiceWithMetadata.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Service binder with metadata.
+ * @hide
+ */
+parcelable ServiceWithMetadata {
+ /**
+ * IBinder to service
+ */
+ @nullable IBinder service;
+ /**
+ * boolean if the IBinder can be cached by client.
+ */
+ boolean isLazyService;
+}
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 81f7cdb..7d79baa 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -150,6 +150,12 @@
int pid;
};
virtual std::vector<ServiceDebugInfo> getServiceDebugInfo() = 0;
+
+ /**
+ * Directly enable or disable caching binder during addService calls.
+ * Only used for testing. This is enabled by default.
+ */
+ virtual void enableAddServiceCache(bool value) = 0;
};
LIBBINDER_EXPORTED sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index cc4943b..f5df8d5 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -34,6 +34,7 @@
ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2,
ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3,
ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4,
+ // All other bits are reserved for internal usage
};
/**
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 28a3f65..c21d7c6 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -70,7 +70,11 @@
static_libs: [
"libfakeservicemanager",
],
- defaults: ["libbinder_client_cache_flag"],
+ defaults: [
+ "libbinder_client_cache_flag",
+ "libbinder_addservice_cache_flag",
+ "libbinder_remove_cache_static_list_flag",
+ ],
test_suites: ["general-tests"],
require_root: true,
}
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index c5ad793..19395c2 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -34,6 +34,18 @@
constexpr bool kUseLibbinderCache = false;
#endif
+#ifdef LIBBINDER_ADDSERVICE_CACHE
+constexpr bool kUseCacheInAddService = true;
+#else
+constexpr bool kUseCacheInAddService = false;
+#endif
+
+#ifdef LIBBINDER_REMOVE_CACHE_STATIC_LIST
+constexpr bool kRemoveStaticList = true;
+#else
+constexpr bool kRemoveStaticList = false;
+#endif
+
// A service name which is in the static list of cachable services
const String16 kCachedServiceName = String16("isub");
@@ -63,8 +75,10 @@
MockAidlServiceManager() : innerSm() {}
binder::Status checkService(const ::std::string& name, os::Service* _out) override {
- sp<IBinder> binder = innerSm.getService(String16(name.c_str()));
- *_out = os::Service::make<os::Service::Tag::binder>(binder);
+ os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
+ serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
+ serviceWithMetadata.isLazyService = false;
+ *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(serviceWithMetadata);
return binder::Status::ok();
}
@@ -74,14 +88,127 @@
innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority));
}
+ void clearServices() { innerSm.clear(); }
+
FakeServiceManager innerSm;
};
+// Returns services with isLazyService flag as true.
+class MockAidlServiceManager2 : public os::IServiceManagerDefault {
+public:
+ MockAidlServiceManager2() : innerSm() {}
+
+ binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+ os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
+ serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
+ serviceWithMetadata.isLazyService = true;
+ *_out = os::Service::make<os::Service::Tag::serviceWithMetadata>(serviceWithMetadata);
+ return binder::Status::ok();
+ }
+
+ binder::Status addService(const std::string& name, const sp<IBinder>& service,
+ bool allowIsolated, int32_t dumpPriority) override {
+ return binder::Status::fromStatusT(
+ innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority));
+ }
+
+ void clearServices() { innerSm.clear(); }
+
+ FakeServiceManager innerSm;
+};
+
+class LibbinderCacheRemoveStaticList : public ::testing::Test {
+protected:
+ void SetUp() override {
+ fakeServiceManager = sp<MockAidlServiceManager2>::make();
+ mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(fakeServiceManager);
+ mServiceManager->enableAddServiceCache(true);
+ }
+ void TearDown() override {}
+
+public:
+ void cacheAddServiceAndConfirmCacheMiss(const sp<IBinder>& binder1) {
+ // Add a service. This shouldn't cache it.
+ EXPECT_EQ(OK,
+ mServiceManager->addService(kCachedServiceName, binder1,
+ /*allowIsolated = */ false,
+ android::os::IServiceManager::FLAG_IS_LAZY_SERVICE));
+ // Try to populate cache. Cache shouldn't be updated.
+ EXPECT_EQ(binder1, mServiceManager->checkService(kCachedServiceName));
+ fakeServiceManager->clearServices();
+ EXPECT_EQ(nullptr, mServiceManager->checkService(kCachedServiceName));
+ }
+
+ sp<MockAidlServiceManager2> fakeServiceManager;
+ sp<android::IServiceManager> mServiceManager;
+};
+
+TEST_F(LibbinderCacheRemoveStaticList, AddLocalServiceAndConfirmCacheMiss) {
+ if (!kRemoveStaticList) {
+ GTEST_SKIP() << "Skipping as feature is not enabled";
+ return;
+ }
+ sp<IBinder> binder1 = sp<BBinder>::make();
+ cacheAddServiceAndConfirmCacheMiss(binder1);
+}
+
+TEST_F(LibbinderCacheRemoveStaticList, AddRemoteServiceAndConfirmCacheMiss) {
+ if (!kRemoveStaticList) {
+ GTEST_SKIP() << "Skipping as feature is not enabled";
+ return;
+ }
+ sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName);
+ ASSERT_NE(binder1, nullptr);
+ cacheAddServiceAndConfirmCacheMiss(binder1);
+}
+
+class LibbinderCacheAddServiceTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ fakeServiceManager = sp<MockAidlServiceManager>::make();
+ mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(fakeServiceManager);
+ mServiceManager->enableAddServiceCache(true);
+ }
+
+ void TearDown() override {}
+
+public:
+ void cacheAddServiceAndConfirmCacheHit(const sp<IBinder>& binder1) {
+ // Add a service. This also caches it.
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder1));
+ // remove services from fakeservicemanager
+ fakeServiceManager->clearServices();
+
+ sp<IBinder> result = mServiceManager->checkService(kCachedServiceName);
+ if (kUseCacheInAddService && kUseLibbinderCache) {
+ // If cache is enabled, we should get the binder.
+ EXPECT_EQ(binder1, result);
+ } else {
+ // If cache is disabled, then we should get the null binder
+ EXPECT_EQ(nullptr, result);
+ }
+ }
+ sp<MockAidlServiceManager> fakeServiceManager;
+ sp<android::IServiceManager> mServiceManager;
+};
+
+TEST_F(LibbinderCacheAddServiceTest, AddLocalServiceAndConfirmCacheHit) {
+ sp<IBinder> binder1 = sp<BBinder>::make();
+ cacheAddServiceAndConfirmCacheHit(binder1);
+}
+
+TEST_F(LibbinderCacheAddServiceTest, AddRemoteServiceAndConfirmCacheHit) {
+ sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName);
+ ASSERT_NE(binder1, nullptr);
+ cacheAddServiceAndConfirmCacheHit(binder1);
+}
+
class LibbinderCacheTest : public ::testing::Test {
protected:
void SetUp() override {
- sp<MockAidlServiceManager> sm = sp<MockAidlServiceManager>::make();
- mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(sm);
+ fakeServiceManager = sp<MockAidlServiceManager>::make();
+ mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(fakeServiceManager);
+ mServiceManager->enableAddServiceCache(false);
}
void TearDown() override {}
@@ -108,6 +235,7 @@
}
}
+ sp<MockAidlServiceManager> fakeServiceManager;
sp<android::IServiceManager> mServiceManager;
};
@@ -180,7 +308,13 @@
EXPECT_EQ(binder2, result);
}
+// TODO(b/333854840): Remove this test removing the static list
TEST_F(LibbinderCacheTest, DoNotCacheServiceNotInList) {
+ if (kRemoveStaticList) {
+ GTEST_SKIP() << "Skipping test as static list is disabled";
+ return;
+ }
+
sp<IBinder> binder1 = sp<BBinder>::make();
sp<IBinder> binder2 = sp<BBinder>::make();
String16 serviceName = String16("NewLibbinderCacheTest");
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 970852c..9e92f95 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -223,6 +223,8 @@
ASSERT_GT(m_serverpid, 0);
sp<IServiceManager> sm = defaultServiceManager();
+ // disable caching during addService.
+ sm->enableAddServiceCache(false);
//printf("%s: pid %d, get service\n", __func__, m_pid);
LIBBINDER_IGNORE("-Wdeprecated-declarations")
m_server = sm->getService(binderLibTestServiceName);
@@ -298,6 +300,9 @@
virtual void SetUp() {
m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
IPCThreadState::self()->restoreCallingWorkSource(0);
+ sp<IServiceManager> sm = defaultServiceManager();
+ // disable caching during addService.
+ sm->enableAddServiceCache(false);
}
virtual void TearDown() {
}
@@ -1765,6 +1770,7 @@
TEST(ServiceNotifications, Unregister) {
auto sm = defaultServiceManager();
+ sm->enableAddServiceCache(false);
using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
void onServiceRegistration(const String16 &, const sp<IBinder> &) override {}
@@ -2529,6 +2535,8 @@
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
+ sm->enableAddServiceCache(false);
+
BinderLibTestService* testServicePtr;
{
sp<BinderLibTestService> testService = new BinderLibTestService(index);
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index f62241d..f2b2aa7 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -65,6 +65,7 @@
std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override;
+ void enableAddServiceCache(bool /*value*/) override {}
// Clear all of the registered services
void clear();
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 0c8f3fa..ba50bf8 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -369,6 +369,10 @@
this, to_string(displayId).c_str(), connectedLevel, maxLevel);
}
+void Choreographer::dispatchModeRejected(PhysicalDisplayId, int32_t) {
+ LOG_ALWAYS_FATAL("dispatchModeRejected was called but was never registered");
+}
+
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
case MSG_SCHEDULE_CALLBACKS:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index c46f9c5..68f10f4 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -211,6 +211,9 @@
ev.hdcpLevelsChange.connectedLevel,
ev.hdcpLevelsChange.maxLevel);
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ dispatchModeRejected(ev.header.displayId, ev.modeRejection.modeId);
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
index 01aa7ed..5c4879c 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -42,7 +42,7 @@
if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE &&
- compatibility != ANATIVEWINDOW_FRAME_RATE_GTE &&
+ compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE &&
(!privileged ||
(compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT &&
compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) {
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 2e5aa4a..a93ba14 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -127,6 +127,7 @@
std::vector<FrameRateOverride> overrides) override;
void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) override;
+ void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) override;
void scheduleCallbacks();
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 82cd50c..b06ad07 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -68,6 +68,8 @@
virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) = 0;
+ virtual void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) = 0;
+
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 40a6e79..ab6a6b7 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -62,6 +62,7 @@
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
+ DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'),
DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
@@ -96,6 +97,10 @@
nsecs_t vsyncPeriod __attribute__((aligned(8)));
};
+ struct ModeRejection {
+ int32_t modeId;
+ };
+
struct FrameRateOverride {
uid_t uid __attribute__((aligned(8)));
float frameRateHz __attribute__((aligned(8)));
@@ -117,6 +122,7 @@
ModeChange modeChange;
FrameRateOverride frameRateOverride;
HdcpLevelsChange hdcpLevelsChange;
+ ModeRejection modeRejection;
};
};
static_assert(sizeof(Event) == 224);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c77a30a..06f00a4 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -77,7 +77,7 @@
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
- ALOGE("Linked to input");
+ ALOGI("Linked to input");
}
return interface_cast<IInputFlinger>(input);
}
@@ -118,7 +118,7 @@
} else {
android::os::InputChannelCore tempChannel;
android::binder::Status result =
- mInputFlinger->createInputChannel("testchannels", &tempChannel);
+ mInputFlinger->createInputChannel(sc->getName() + " channel", &tempChannel);
if (!result.isOk()) {
ADD_FAILURE() << "binder call to createInputChannel failed";
}
@@ -141,47 +141,51 @@
}
static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc,
- int width, int height) {
+ int width, int height,
+ const std::string& name) {
sp<SurfaceControl> surfaceControl =
- scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
+ scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect);
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
static std::unique_ptr<InputSurface> makeBufferInputSurface(
- const sp<SurfaceComposerClient>& scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height,
+ const std::string& name = "Test Buffer Surface") {
sp<SurfaceControl> surfaceControl =
- scc->createSurface(String8("Test Buffer Surface"), width, height,
- PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+ scc->createSurface(String8(name.c_str()), width, height, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */);
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
static std::unique_ptr<InputSurface> makeContainerInputSurface(
- const sp<SurfaceComposerClient>& scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height,
+ const std::string& name = "Test Container Surface") {
sp<SurfaceControl> surfaceControl =
- scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
- 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */,
+ PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceContainer);
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
static std::unique_ptr<InputSurface> makeContainerInputSurfaceNoInputChannel(
- const sp<SurfaceComposerClient>& scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height,
+ const std::string& name = "Test Container Surface") {
sp<SurfaceControl> surfaceControl =
- scc->createSurface(String8("Test Container Surface"), 100 /* height */,
- 100 /* width */, PIXEL_FORMAT_RGBA_8888,
+ scc->createSurface(String8(name.c_str()), 100 /* height */, 100 /* width */,
+ PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceContainer);
return std::make_unique<InputSurface>(surfaceControl, width, height,
true /* noInputChannel */);
}
static std::unique_ptr<InputSurface> makeCursorInputSurface(
- const sp<SurfaceComposerClient>& scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height,
+ const std::string& name = "Test Cursor Surface") {
sp<SurfaceControl> surfaceControl =
- scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
- 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eCursorWindow);
+ scc->createSurface(String8(name.c_str()), 0 /* bufHeight */, 0 /* bufWidth */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eCursorWindow);
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
@@ -412,8 +416,9 @@
void TearDown() { mComposerClient->dispose(); }
- std::unique_ptr<InputSurface> makeSurface(int width, int height) {
- return InputSurface::makeColorInputSurface(mComposerClient, width, height);
+ std::unique_ptr<InputSurface> makeSurface(int width, int height,
+ const std::string& name = "Test Surface") const {
+ return InputSurface::makeColorInputSurface(mComposerClient, width, height, name);
}
void postBuffer(const sp<SurfaceControl>& layer, int32_t w, int32_t h) {
@@ -472,10 +477,10 @@
* reverse order.
*/
TEST_F(InputSurfacesTest, input_respects_positioning) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Left Surface");
surface->showAt(100, 100);
- std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100, "Right Surface");
surface2->showAt(200, 200);
injectTap(201, 201);
@@ -495,8 +500,8 @@
}
TEST_F(InputSurfacesTest, input_respects_layering) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface 1");
+ std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100, "Test Surface 2");
surface->showAt(10, 10);
surface2->showAt(10, 10);
@@ -521,8 +526,8 @@
// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
// of the client content.
TEST_F(InputSurfacesTest, input_respects_surface_insets) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(100, 100);
fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
@@ -536,8 +541,8 @@
}
TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableRegionWithCrop) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(100, 100);
fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
@@ -553,8 +558,8 @@
// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
- std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100, "Parent Surface");
+ std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100, "Child Surface");
parentSurface->showAt(100, 100);
childSurface->mInputInfo->editInfo()->surfaceInset = 10;
@@ -574,8 +579,8 @@
// Ensure a surface whose insets are scaled, handles the touch offset correctly.
TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(100, 100);
fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
@@ -592,8 +597,8 @@
}
TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(100, 100);
// In case we pass the very big inset without any checking.
@@ -623,8 +628,8 @@
}
TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(100, 100);
// Set the touchable region to the values at the limit of its corresponding type.
@@ -643,8 +648,8 @@
}
TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(0, 0);
fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
@@ -709,8 +714,8 @@
}
TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) {
- std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100, "Background Surface");
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100, "Foreground Surface");
bgSurface->showAt(10, 10);
fgSurface->showAt(10, 10);
@@ -841,12 +846,13 @@
}
TEST_F(InputSurfacesTest, touch_flag_obscured) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Touchable Surface");
surface->showAt(100, 100);
// Add non touchable window to fully cover touchable window. Window behind gets touch, but
// with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
- std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface =
+ makeSurface(100, 100, "Non-Touchable Surface");
nonTouchableSurface->mInputInfo->editInfo()
->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
@@ -860,14 +866,15 @@
}
TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
surface->showAt(100, 100);
// Add non touchable window to cover touchable window, but parent is cropped to not cover area
// that will be tapped. Window behind gets touch, but with flag
// AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
- std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100, "Parent Surface");
+ std::unique_ptr<InputSurface> nonTouchableSurface =
+ makeSurface(100, 100, "Non-Touchable Surface");
nonTouchableSurface->mInputInfo->editInfo()
->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
@@ -887,13 +894,14 @@
}
TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
surface->showAt(100, 100);
// Add non touchable window to cover touchable window, but parent is cropped to avoid covering
// the touchable window. Window behind gets touch with no obscured flags.
- std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
- std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100, "Parent Surface");
+ std::unique_ptr<InputSurface> nonTouchableSurface =
+ makeSurface(100, 100, "Non-Touchable Surface");
nonTouchableSurface->mInputInfo->editInfo()
->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
@@ -977,12 +985,12 @@
}
TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
- std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100, "Obscuring Surface");
obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
true);
obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
@@ -997,12 +1005,12 @@
}
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
- std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100, "Obscuring Surface");
obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
true);
obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
@@ -1019,10 +1027,10 @@
}
TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
- std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300, "Parent Surface");
parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
surface->showAt(100, 100);
surface->doTransaction([&](auto& t, auto& sc) {
t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
@@ -1041,10 +1049,10 @@
}
TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
- std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300, "Parent Surface");
parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
- std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100, "Test Surface");
surface->doTransaction([&](auto& t, auto& sc) {
t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
t.reparent(sc, parentSurface->mSurfaceControl);
@@ -1107,9 +1115,11 @@
*/
TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) {
std::unique_ptr<InputSurface> parentContainer =
- InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0,
+ "Parent Container Surface");
std::unique_ptr<InputSurface> containerSurface =
- InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100,
+ "Test Container Surface");
containerSurface->doTransaction(
[&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
@@ -1132,11 +1142,14 @@
*/
TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) {
std::unique_ptr<InputSurface> bgContainer =
- InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0,
+ "Background Container Surface");
std::unique_ptr<InputSurface> parentContainer =
- InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0,
+ "Parent Container Surface");
std::unique_ptr<InputSurface> containerSurface =
- InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100,
+ "Test Container Surface");
containerSurface->doTransaction(
[&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
@@ -1162,11 +1175,11 @@
*/
TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) {
std::unique_ptr<InputSurface> cropLayer =
- InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0, "Crop Layer Surface");
cropLayer->showAt(50, 50, Rect(0, 0, 20, 20));
std::unique_ptr<InputSurface> containerSurface =
- InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100, "Container Surface");
containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle =
cropLayer->mSurfaceControl->getHandle();
diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
index 04bfb28..9ffe91f 100644
--- a/libs/gui/tests/FrameRateUtilsTest.cpp
+++ b/libs/gui/tests/FrameRateUtilsTest.cpp
@@ -34,7 +34,7 @@
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_GTE,
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
// Privileged APIs.
@@ -62,7 +62,7 @@
// 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, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, 3, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
// Invalid change frame rate strategy.
if (flags::bq_setframerate()) {
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 6f816bf..ed3e8c1 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -243,8 +243,7 @@
* There are no inherent restrictions on the frame rate of this window. When
* the system selects a frame rate other than what the app requested, the
* app will be able to run at the system frame rate without requiring pull
- * down. This value should be used when displaying game content, UIs, and
- * anything that isn't video.
+ * down. This value should be used when displaying game content.
*/
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
/**
@@ -256,7 +255,14 @@
* stuttering) than it would be if the system had chosen the app's requested
* frame rate. This value should be used for video content.
*/
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1,
+
+ /**
+ * The window requests a frame rate that is greater than or equal to the specified frame rate.
+ * This value should be used for UIs, animations, scrolling, and anything that is not a game
+ * or video.
+ */
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE = 2
};
/**
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 33c303a..f669f77 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1060,12 +1060,7 @@
/**
* This surface will vote for the minimum refresh rate.
*/
- ANATIVEWINDOW_FRAME_RATE_MIN,
-
- /**
- * The surface requests a frame rate that is greater than or equal to `frameRate`.
- */
- ANATIVEWINDOW_FRAME_RATE_GTE
+ ANATIVEWINDOW_FRAME_RATE_MIN
};
/*
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 9876362..d760285 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -30,8 +30,8 @@
StatusCode,
};
use ffi::{
- AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel,
- AHardwareBuffer_writeToParcel, ARect,
+ AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_Plane, AHardwareBuffer_Planes,
+ AHardwareBuffer_readFromParcel, AHardwareBuffer_writeToParcel, ARect,
};
use std::ffi::c_void;
use std::fmt::{self, Debug, Formatter};
@@ -313,6 +313,57 @@
})
}
+ /// Lock a potentially multi-planar hardware buffer for direct CPU access.
+ ///
+ /// # Safety
+ ///
+ /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+ /// before calling this function.
+ /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+ /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+ /// and break Rust's aliasing rules, like any other shared memory.
+ /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+ /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+ /// processes lock the buffer simultaneously for any usage.
+ /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+ /// simultaneously.
+ /// - If `rect` is not `None`, the caller must not modify the buffer outside of that rectangle.
+ pub unsafe fn lock_planes<'a>(
+ &'a self,
+ usage: AHardwareBuffer_UsageFlags,
+ fence: Option<BorrowedFd>,
+ rect: Option<&ARect>,
+ ) -> Result<Vec<PlaneGuard<'a>>, StatusCode> {
+ let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+ let rect = rect.map(ptr::from_ref).unwrap_or(null());
+ let mut planes = AHardwareBuffer_Planes {
+ planeCount: 0,
+ planes: [const { AHardwareBuffer_Plane { data: null_mut(), pixelStride: 0, rowStride: 0 } };
+ 4],
+ };
+
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the various out
+ // pointers are valid because they come from references. Our caller promises that writes have
+ // completed and there will be no simultaneous read/write locks.
+ let status = unsafe {
+ ffi::AHardwareBuffer_lockPlanes(self.0.as_ptr(), usage.0, fence, rect, &mut planes)
+ };
+ status_result(status)?;
+ let plane_count = planes.planeCount.try_into().unwrap();
+ Ok(planes.planes[..plane_count]
+ .iter()
+ .map(|plane| PlaneGuard {
+ guard: HardwareBufferGuard {
+ buffer: self,
+ address: NonNull::new(plane.data)
+ .expect("AHardwareBuffer_lockAndGetInfo set a null outVirtualAddress"),
+ },
+ pixel_stride: plane.pixelStride,
+ row_stride: plane.rowStride,
+ })
+ .collect())
+ }
+
/// Locks the hardware buffer for direct CPU access, returning information about the bytes per
/// pixel and stride as well.
///
@@ -510,6 +561,18 @@
pub stride: u32,
}
+/// A guard for a single plane of a locked `HardwareBuffer`, with additional information about the
+/// stride.
+#[derive(Debug)]
+pub struct PlaneGuard<'a> {
+ /// The locked buffer guard.
+ pub guard: HardwareBufferGuard<'a>,
+ /// The stride in bytes between the color channel for one pixel to the next pixel.
+ pub pixel_stride: u32,
+ /// The stride in bytes between rows in the buffer.
+ pub row_stride: u32,
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
index db0b133..da47aae 100644
--- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -96,13 +96,17 @@
void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input,
const float inverseScale, const float radius,
const float alpha) const {
- SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("child") = std::move(input);
- blurBuilder.uniform("in_inverseScale") = inverseScale;
- blurBuilder.uniform("in_blurOffset") = radius;
- blurBuilder.uniform("in_crossFade") = alpha;
SkPaint paint;
- paint.setShader(blurBuilder.makeShader(nullptr));
+ if (radius == 0) {
+ paint.setShader(std::move(input));
+ paint.setAlphaf(alpha);
+ } else {
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") = std::move(input);
+ blurBuilder.uniform("in_blurOffset") = radius;
+ blurBuilder.uniform("in_crossFade") = alpha;
+ paint.setShader(blurBuilder.makeShader(nullptr));
+ }
paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
drawSurface->getCanvas()->drawPaint(paint);
}
@@ -116,32 +120,35 @@
// Use a variable number of blur passes depending on the radius. The non-integer part of this
// calculation is used to mix the final pass into the second-last with an alpha blend.
- constexpr int kMaxSurfaces = 4;
- const float filterDepth =
- std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale)));
+ constexpr int kMaxSurfaces = 3;
+ const float filterDepth = std::min(kMaxSurfaces - 1.0f, radius * kInputScale / 2.5f);
const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));
- // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale.
+ // Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale.
sk_sp<SkSurface> surfaces[kMaxSurfaces] =
{filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
- filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
- filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
- filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
+ filterPasses >= 1 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
+ filterPasses >= 2 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
- // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600.
- static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f};
+ // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 250.
+ static const float kWeights[5] = {
+ 1.0f, // 1st downsampling pass
+ 1.0f, // 2nd downsampling pass
+ 1.0f, // 3rd downsampling pass
+ 0.0f, // 1st upscaling pass. Set to zero to upscale without blurring for performance.
+ 1.0f, // 2nd upscaling pass
+ };
// Kawase is an approximation of Gaussian, but behaves differently because it is made up of many
// simpler blurs. A transformation is required to approximate the same effect as Gaussian.
- float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f);
+ float sumSquaredR = powf(kWeights[0], 2.0f);
for (int i = 0; i < filterPasses; i++) {
const float alpha = std::min(1.0f, filterDepth - i);
- sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f);
- sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f);
+ sumSquaredR += powf(powf(2.0f, i) * alpha * kWeights[1 + i] / kInputScale, 2.0f);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[4 - i] / kInputScale, 2.0f);
}
- // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets.
- const float step = M_SQRT1_2 *
- sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR));
+ // Solve for R = sqrt(sum(r_i^2)).
+ const float step = radius * sqrt(1.0f / sumSquaredR);
// Start by downscaling and doing the first blur pass.
{
@@ -162,7 +169,7 @@
}
// Finally blur+upscale back to our original size.
for (int i = filterPasses - 1; i >= 0; i--) {
- blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step,
+ blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[4 - i] * step,
std::min(1.0f, filterDepth - i));
}
return surfaces[0]->makeImageSnapshot();
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index c4f8663..a58bc77 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -236,6 +236,7 @@
std::call_once(registration, [test]() {
struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM;
+ args.shmem_size_hint_kb = 1024;
PerfettoProducerInit(args);
PerfettoTeInit();
PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
index c9663ed..dbc0884 100644
--- a/libs/ui/PublicFormat.cpp
+++ b/libs/ui/PublicFormat.cpp
@@ -30,6 +30,7 @@
case PublicFormat::DEPTH_POINT_CLOUD:
case PublicFormat::DEPTH_JPEG:
case PublicFormat::HEIC:
+ case PublicFormat::HEIC_ULTRAHDR:
case PublicFormat::JPEG_R:
return HAL_PIXEL_FORMAT_BLOB;
case PublicFormat::DEPTH16:
@@ -74,6 +75,9 @@
case PublicFormat::HEIC:
dataspace = Dataspace::HEIF;
break;
+ case PublicFormat::HEIC_ULTRAHDR:
+ dataspace = Dataspace::HEIF_ULTRAHDR;
+ break;
case PublicFormat::JPEG_R:
dataspace = Dataspace::JPEG_R;
break;
@@ -153,6 +157,9 @@
return PublicFormat::DEPTH_JPEG;
} else if (dataSpace == static_cast<android_dataspace>(Dataspace::JPEG_R)) {
return PublicFormat::JPEG_R;
+ } else if (dataSpace == static_cast<android_dataspace>(
+ Dataspace::HEIF_ULTRAHDR)) {
+ return PublicFormat::HEIC_ULTRAHDR;
}else {
// Assume otherwise-marked blobs are also JPEG
return PublicFormat::JPEG;
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index 2248cca..e87931e 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -59,6 +59,7 @@
DEPTH_JPEG = 0x69656963,
JPEG_R = 0x1005,
HEIC = 0x48454946,
+ HEIC_ULTRAHDR = 0x1006,
};
/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 8eb6bdd..811692f 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -64,9 +64,8 @@
!isFromSource(sources, AINPUT_SOURCE_STYLUS));
}
-inline void notifyPointerDisplayChange(
- std::optional<std::tuple<ui::LogicalDisplayId, FloatPoint>> change,
- PointerChoreographerPolicyInterface& policy) {
+inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change,
+ PointerChoreographerPolicyInterface& policy) {
if (!change) {
return;
}
@@ -245,9 +244,9 @@
if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
// This is an absolute mouse device that knows about the location of the cursor on the
// display, so set the cursor position to the specified location.
- const auto [x, y] = pc.getPosition();
- const float deltaX = args.xCursorPosition - x;
- const float deltaY = args.yCursorPosition - y;
+ const auto position = pc.getPosition();
+ const float deltaX = args.xCursorPosition - position.x;
+ const float deltaY = args.yCursorPosition - position.y;
newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
pc.setPosition(args.xCursorPosition, args.yCursorPosition);
@@ -273,15 +272,15 @@
processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
} else {
// This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
- const auto [x, y] = pc.getPosition();
+ const auto position = pc.getPosition();
for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
- args.pointerCoords[i].getX() + x);
+ args.pointerCoords[i].getX() + position.x);
newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
- args.pointerCoords[i].getY() + y);
+ args.pointerCoords[i].getY() + position.y);
}
- newArgs.xCursorPosition = x;
- newArgs.yCursorPosition = y;
+ newArgs.xCursorPosition = position.x;
+ newArgs.yCursorPosition = position.y;
}
// Note displayId may have changed if the cursor moved to a different display
@@ -296,7 +295,7 @@
const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- FloatPoint unconsumedDelta = pc.move(deltaX, deltaY);
+ vec2 unconsumedDelta = pc.move(deltaX, deltaY);
if (com::android::input::flags::connected_displays_cursor() &&
(std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
handleUnconsumedDeltaLocked(pc, unconsumedDelta);
@@ -304,25 +303,23 @@
newArgs.displayId = pc.getDisplayId();
}
- const auto [x, y] = pc.getPosition();
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- newArgs.xCursorPosition = x;
- newArgs.yCursorPosition = y;
+ const auto position = pc.getPosition();
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
+ newArgs.xCursorPosition = position.x;
+ newArgs.yCursorPosition = position.y;
}
void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterface& pc,
- const FloatPoint& unconsumedDelta) {
+ const vec2& unconsumedDelta) {
// Display topology is in rotated coordinate space and Pointer controller returns and expects
// values in the un-rotated coordinate space. So we need to transform delta and cursor position
// back to the rotated coordinate space to lookup adjacent display in the display topology.
const auto& sourceDisplayTransform = pc.getDisplayTransform();
const vec2 rotatedUnconsumedDelta =
- transformWithoutTranslation(sourceDisplayTransform,
- {unconsumedDelta.x, unconsumedDelta.y});
- const FloatPoint cursorPosition = pc.getPosition();
- const vec2 rotatedCursorPosition =
- sourceDisplayTransform.transform(cursorPosition.x, cursorPosition.y);
+ transformWithoutTranslation(sourceDisplayTransform, unconsumedDelta);
+ const vec2 cursorPosition = pc.getPosition();
+ const vec2 rotatedCursorPosition = sourceDisplayTransform.transform(cursorPosition);
// To find out the boundary that cursor is crossing we are checking delta in x and y direction
// respectively. This prioritizes x direction over y.
@@ -769,7 +766,7 @@
PointerChoreographer::PointerDisplayChange
PointerChoreographer::calculatePointerDisplayChangeToNotify() {
ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
- FloatPoint cursorPosition = {0, 0};
+ vec2 cursorPosition = {0, 0};
if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
it != mMousePointersByDisplay.end()) {
const auto& pointerController = it->second;
@@ -840,7 +837,7 @@
return std::nullopt;
}
-FloatPoint PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
+vec2 PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
std::scoped_lock _l(getLock());
const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 4ca7323..939529f 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -59,7 +59,7 @@
virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0;
virtual std::optional<DisplayViewport> getViewportForPointerDevice(
ui::LogicalDisplayId associatedDisplayId = ui::LogicalDisplayId::INVALID) = 0;
- virtual FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId) = 0;
+ virtual vec2 getMouseCursorPosition(ui::LogicalDisplayId displayId) = 0;
virtual void setShowTouchesEnabled(bool enabled) = 0;
virtual void setStylusPointerIconEnabled(bool enabled) = 0;
/**
@@ -96,7 +96,7 @@
void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override;
std::optional<DisplayViewport> getViewportForPointerDevice(
ui::LogicalDisplayId associatedDisplayId) override;
- FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId) override;
+ vec2 getMouseCursorPosition(ui::LogicalDisplayId displayId) override;
void setShowTouchesEnabled(bool enabled) override;
void setStylusPointerIconEnabled(bool enabled) override;
bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
@@ -134,8 +134,8 @@
void dump(std::string& dump) override;
private:
- using PointerDisplayChange = std::optional<
- std::tuple<ui::LogicalDisplayId /*displayId*/, FloatPoint /*cursorPosition*/>>;
+ using PointerDisplayChange =
+ std::optional<std::tuple<ui::LogicalDisplayId /*displayId*/, vec2 /*cursorPosition*/>>;
// PointerChoreographer's DisplayInfoListener can outlive the PointerChoreographer because when
// the listener is registered and called from display thread, a strong pointer to the listener
@@ -171,8 +171,8 @@
const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
REQUIRES(getLock());
- void handleUnconsumedDeltaLocked(PointerControllerInterface& pc,
- const FloatPoint& unconsumedDelta) REQUIRES(getLock());
+ void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
+ REQUIRES(getLock());
void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index e1f8fda..36614b2 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -54,7 +54,7 @@
* @param position The new position of the mouse cursor on the logical display
*/
virtual void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
- const FloatPoint& position) = 0;
+ const vec2& position) = 0;
/* Returns true if any InputConnection is currently active. */
virtual bool isInputMethodConnectionActive() = 0;
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index abca209..bd46490 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -24,20 +24,6 @@
struct SpriteIcon;
-struct FloatPoint {
- float x;
- float y;
-
- inline FloatPoint(float x, float y) : x(x), y(y) {}
-
- inline explicit FloatPoint(vec2 p) : x(p.x), y(p.y) {}
-
- template <typename T, typename U>
- operator std::tuple<T, U>() {
- return {x, y};
- }
-};
-
/**
* Interface for tracking a mouse / touch pad pointer and touch pad spots.
*
@@ -77,13 +63,13 @@
*
* Return value may be used to move pointer to corresponding adjacent display, if it exists in
* the display-topology */
- [[nodiscard]] virtual FloatPoint move(float deltaX, float deltaY) = 0;
+ [[nodiscard]] virtual vec2 move(float deltaX, float deltaY) = 0;
/* Sets the absolute location of the pointer. */
virtual void setPosition(float x, float y) = 0;
/* Gets the absolute location of the pointer. */
- virtual FloatPoint getPosition() const = 0;
+ virtual vec2 getPosition() const = 0;
enum class Transition {
// Fade/unfade immediately.
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index f53e63b..f033e57 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -43,7 +43,7 @@
mY = y;
}
-FloatPoint FakePointerController::getPosition() const {
+vec2 FakePointerController::getPosition() const {
if (!mEnabled) {
return {0, 0};
}
@@ -96,9 +96,9 @@
}
void FakePointerController::assertPosition(float x, float y) {
- const auto [actualX, actualY] = getPosition();
- ASSERT_NEAR(x, actualX, 1);
- ASSERT_NEAR(y, actualY, 1);
+ const auto actual = getPosition();
+ ASSERT_NEAR(x, actual.x, 1);
+ ASSERT_NEAR(y, actual.y, 1);
}
void FakePointerController::assertSpotCount(ui::LogicalDisplayId displayId, int32_t count) {
@@ -148,13 +148,13 @@
return mIsPointerShown;
}
-FloatPoint FakePointerController::move(float deltaX, float deltaY) {
+vec2 FakePointerController::move(float deltaX, float deltaY) {
if (!mEnabled) return {0, 0};
mX += deltaX;
mY += deltaY;
- const FloatPoint position(mX, mY);
+ const vec2 position(mX, mY);
if (mX < mMinX) mX = mMinX;
if (mX > mMaxX) mX = mMaxX;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 0ee3123..c526bb8 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -40,7 +40,7 @@
const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& getSpots();
void setPosition(float x, float y) override;
- FloatPoint getPosition() const override;
+ vec2 getPosition() const override;
ui::LogicalDisplayId getDisplayId() const override;
void setDisplayViewport(const DisplayViewport& viewport) override;
void updatePointerIcon(PointerIconStyle iconId) override;
@@ -66,7 +66,7 @@
private:
std::string dump() override { return ""; }
- FloatPoint move(float deltaX, float deltaY) override;
+ vec2 move(float deltaX, float deltaY) override;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 6f7c2e5..ac616d0 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -188,7 +188,7 @@
MOCK_METHOD(std::shared_ptr<PointerControllerInterface>, createPointerController,
(PointerControllerInterface::ControllerType), (override));
MOCK_METHOD(void, notifyPointerDisplayIdChanged,
- (ui::LogicalDisplayId displayId, const FloatPoint& position), (override));
+ (ui::LogicalDisplayId displayId, const vec2& position), (override));
MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
};
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 453c156..27da3d3 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -135,7 +135,7 @@
});
ON_CALL(mMockPolicy, notifyPointerDisplayIdChanged)
- .WillByDefault([this](ui::LogicalDisplayId displayId, const FloatPoint& position) {
+ .WillByDefault([this](ui::LogicalDisplayId displayId, const vec2& position) {
mPointerDisplayIdNotified = displayId;
});
}
@@ -2604,9 +2604,8 @@
using PointerChoreographerDisplayTopologyTestFixtureParam =
std::tuple<std::string_view /*name*/, int32_t /*source device*/,
ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
- FloatPoint /*source position*/, FloatPoint /*hover move X/Y*/,
- ui::LogicalDisplayId /*destination display*/,
- FloatPoint /*destination position*/>;
+ vec2 /*source position*/, vec2 /*hover move X/Y*/,
+ ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>;
class PointerChoreographerDisplayTopologyTestFixture
: public PointerChoreographerTest,
@@ -2708,67 +2707,63 @@
// Note: Upon viewport transition cursor will be positioned at the boundary of the
// destination, as we drop any unconsumed delta.
std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
- ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
- FloatPoint(25, 25) /* delta x/y */,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+ vec2(25, 25) /* delta x/y */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- FloatPoint(75, 75) /* destination x/y */),
- std::make_tuple(
- "TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
- ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
- FloatPoint(100, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID,
- FloatPoint(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
+ vec2(75, 75) /* destination x/y */),
+ std::make_tuple("TransitionToRightDisplay", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ vec2(50, 50) /* initial x/y */, vec2(100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID,
+ vec2(0,
+ 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
std::make_tuple(
"TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
- ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
- FloatPoint(-100, 25) /* delta x/y */,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+ vec2(-100, 25) /* delta x/y */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID,
- FloatPoint(90,
- 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/),
- std::make_tuple(
- "TransitionToTopDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ControllerType::MOUSE, ToolType::FINGER,
- FloatPoint(50, 50) /* initial x/y */, FloatPoint(25, -100) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
- FloatPoint(50 + 25 - 10,
- 90) /* Bottom edge: (source + delta - offset, height) */),
- std::make_tuple(
- "TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ControllerType::MOUSE, ToolType::FINGER,
- FloatPoint(50, 50) /* initial x/y */, FloatPoint(25, 100) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
- FloatPoint(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
+ vec2(90, 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/),
+ std::make_tuple("TransitionToTopDisplay",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+ vec2(25, -100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
+ vec2(50 + 25 - 10,
+ 90) /* Bottom edge: (source + delta - offset, height) */),
+ std::make_tuple("TransitionToBottomDisplay",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+ vec2(25, 100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
+ vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
ControllerType::MOUSE, ToolType::MOUSE,
- FloatPoint(5, 50) /* initial x/y */,
- FloatPoint(0, -100) /* Move Up */,
+ vec2(5, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- FloatPoint(5, 0) /* Top edge */),
+ vec2(5, 0) /* Top edge */),
std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
ControllerType::MOUSE, ToolType::MOUSE,
- FloatPoint(95, 5) /* initial x/y */,
- FloatPoint(100, 0) /* Move Right */,
+ vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- FloatPoint(99, 5) /* Top edge */),
+ vec2(99, 5) /* Top edge */),
std::make_tuple("NoTransitionAtBottomOffset",
AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, FloatPoint(5, 95) /* initial x/y */,
- FloatPoint(0, 100) /* Move Down */,
+ ToolType::FINGER, vec2(5, 95) /* initial x/y */,
+ vec2(0, 100) /* Move Down */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- FloatPoint(5, 99) /* Bottom edge */),
+ vec2(5, 99) /* Bottom edge */),
std::make_tuple("NoTransitionAtLeftOffset",
AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, FloatPoint(5, 5) /* initial x/y */,
- FloatPoint(-100, 0) /* Move Left */,
+ ToolType::FINGER, vec2(5, 5) /* initial x/y */,
+ vec2(-100, 0) /* Move Left */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- FloatPoint(0, 5) /* Left edge */),
+ vec2(0, 5) /* Left edge */),
std::make_tuple(
"TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ControllerType::MOUSE, ToolType::FINGER,
- FloatPoint(95, 5) /* initial x/y */,
- FloatPoint(10, -10) /* Move dignally to top right corner */,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */,
+ vec2(10, -10) /* Move dignally to top right corner */,
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
- FloatPoint(0, 90) /* bottom left corner */)),
+ vec2(0, 90) /* bottom left corner */)),
[](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
return std::string{std::get<0>(p.param)};
});
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 7b2596a..8c80dd8 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -92,7 +92,7 @@
"libaidlcommonsupport",
"android.hardware.sensors@1.0-convert",
"android.hardware.sensors-V1-convert",
- "android.hardware.sensors-V2-ndk",
+ "android.hardware.sensors-V3-ndk",
"sensorservice_flags_c_lib",
],
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
index 542fcae..b9a3491 100644
--- a/services/sensorservice/aidl/Android.bp
+++ b/services/sensorservice/aidl/Android.bp
@@ -28,7 +28,7 @@
"libbinder_ndk",
"libsensor",
"android.frameworks.sensorservice-V1-ndk",
- "android.hardware.sensors-V2-ndk",
+ "android.hardware.sensors-V3-ndk",
],
export_include_dirs: [
"include/",
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index b2dc89b..880df08 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -20,7 +20,7 @@
"android.companion.virtual.virtualdevice_aidl-cpp",
"android.frameworks.sensorservice-V1-ndk",
"android.hardware.sensors-V1-convert",
- "android.hardware.sensors-V2-ndk",
+ "android.hardware.sensors-V3-ndk",
"android.hardware.common-V2-ndk",
"libsensor",
"libfakeservicemanager",
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index fff4284..c6d7160 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,6 +43,7 @@
#include <utils/Errors.h>
#include <common/FlagManager.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/VsyncConfig.h>
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
@@ -102,6 +103,10 @@
to_string(event.header.displayId).c_str(),
event.hdcpLevelsChange.connectedLevel,
event.hdcpLevelsChange.maxLevel);
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ return StringPrintf("ModeRejected{displayId=%s, modeId=%u}",
+ to_string(event.header.displayId).c_str(),
+ event.modeRejection.modeId);
default:
return "Event{}";
}
@@ -186,6 +191,18 @@
};
}
+DisplayEventReceiver::Event makeModeRejection(PhysicalDisplayId displayId, DisplayModeId modeId) {
+ return DisplayEventReceiver::Event{
+ .header =
+ DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ },
+ .modeRejection.modeId = ftl::to_underlying(modeId),
+ };
+}
+
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
@@ -480,6 +497,13 @@
mCondition.notify_all();
}
+void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeModeRejection(displayId, modeId));
+ mCondition.notify_all();
+}
+
// Merge lists of buffer stuffed Uids
void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
std::lock_guard<std::mutex> lock(mMutex);
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 95632c7..18bf416 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -21,6 +21,7 @@
#include <gui/DisplayEventReceiver.h>
#include <private/gui/BitTube.h>
#include <sys/types.h>
+#include <ui/DisplayId.h>
#include <utils/Errors.h>
#include <scheduler/FrameRateMode.h>
@@ -32,6 +33,7 @@
#include <thread>
#include <vector>
+#include "DisplayHardware/DisplayMode.h"
#include "TracedOrdinal.h"
#include "VSyncDispatch.h"
#include "VsyncSchedule.h"
@@ -115,6 +117,9 @@
// called when SF changes the active mode and apps needs to be notified about the change
virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
+ // called when SF rejects the mode change request
+ virtual void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) = 0;
+
// called when SF updates the Frame Rate Override list
virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) = 0;
@@ -179,6 +184,8 @@
void onModeChanged(const scheduler::FrameRateMode&) override;
+ void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) override;
+
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 2b9e88c..d02d149 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -31,7 +31,7 @@
virtual void onChoreographerAttached() = 0;
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
- virtual void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) = 0;
+ virtual void onCommitNotComposited() = 0;
virtual void vrrDisplayIdle(bool idle) = 0;
protected:
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 171342d..630beb0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -280,6 +280,9 @@
case Layer::FrameRateCompatibility::Exact:
return LayerVoteType::ExplicitExact;
case Layer::FrameRateCompatibility::Gte:
+ if (frameRate.isNoVote()) {
+ return LayerVoteType::NoVote;
+ }
if (isVrrDevice) {
return LayerVoteType::ExplicitGte;
} else {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index ff1926e..6e2b943 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -504,7 +504,7 @@
return FrameRateCompatibility::Exact;
case ANATIVEWINDOW_FRAME_RATE_MIN:
return FrameRateCompatibility::Min;
- case ANATIVEWINDOW_FRAME_RATE_GTE:
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE:
return FrameRateCompatibility::Gte;
case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
return FrameRateCompatibility::NoVote;
@@ -562,7 +562,10 @@
}
bool LayerInfo::FrameRate::isNoVote() const {
- return vote.type == FrameRateCompatibility::NoVote;
+ // A desired frame rate greater than or equal to 0 is treated as NoVote.
+ bool isNoVoteGte = FlagManager::getInstance().arr_setframerate_gte_enum() &&
+ vote.type == FrameRateCompatibility::Gte && !vote.rate.isValid();
+ return vote.type == FrameRateCompatibility::NoVote || isNoVoteGte;
}
bool LayerInfo::FrameRate::isValuelessType() const {
@@ -577,7 +580,12 @@
case FrameRateCompatibility::Default:
case FrameRateCompatibility::ExactOrMultiple:
case FrameRateCompatibility::Exact:
+ return false;
case FrameRateCompatibility::Gte:
+ if (isNoVote()) {
+ // Special case: GTE 0 is same as NoVote.
+ return true;
+ }
return false;
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2875650..4da76f6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -242,7 +242,7 @@
if (FlagManager::getInstance().vrr_config()) {
compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId);
}
- mSchedulerCallback.onCommitNotComposited(pacesetterPtr->displayId);
+ mSchedulerCallback.onCommitNotComposited();
return;
}
}
@@ -465,6 +465,12 @@
}
#pragma clang diagnostic pop
+void Scheduler::onDisplayModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+ if (hasEventThreads()) {
+ eventThreadFor(Cycle::Render).onModeRejected(displayId, modeId);
+ }
+}
+
void Scheduler::emitModeChangeIfNeeded() {
if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
ALOGW("No mode change to emit");
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 61c68a9..3fdddac 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,12 +36,14 @@
#include <ftl/non_null.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncConfig.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>
+#include "DisplayHardware/DisplayMode.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "ISchedulerCallback.h"
@@ -153,6 +155,8 @@
bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&,
bool clearContentRequirements) EXCLUDES(mPolicyLock);
+ void onDisplayModeRejected(PhysicalDisplayId, DisplayModeId);
+
void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3d5002e..59b1917 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1544,9 +1544,15 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
- constraints, outTimeline) !=
- display::DisplayModeController::ModeChangeResult::Changed) {
+ const auto error =
+ mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+ constraints, outTimeline);
+ if (error != display::DisplayModeController::ModeChangeResult::Changed) {
+ dropModeRequest(displayId);
+ if (FlagManager::getInstance().display_config_error_hal() &&
+ error == display::DisplayModeController::ModeChangeResult::Rejected) {
+ mScheduler->onDisplayModeRejected(displayId, desiredModeId);
+ }
continue;
}
@@ -4404,7 +4410,7 @@
scheduleNotifyExpectedPresentHint(displayId);
}
-void SurfaceFlinger::onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) {
+void SurfaceFlinger::onCommitNotComposited() {
if (FlagManager::getInstance().commit_not_composited()) {
mFrameTimeline->onCommitNotComposited();
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b20a894..1e2c087 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -701,7 +701,7 @@
void onChoreographerAttached() override;
void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
Fps renderRate) override;
- void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) override
+ void onCommitNotComposited() override
REQUIRES(kMainThreadContext);
void vrrDisplayIdle(bool idle) override;
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index f9c99bf..8786f6e 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -38,6 +38,7 @@
],
static_libs: [
"libsurfaceflingerflags",
+ "aconfig_hardware_flags_c_lib",
"android.os.flags-aconfig-cc",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
@@ -51,6 +52,7 @@
],
static_libs: [
"libsurfaceflingerflags_test",
+ "aconfig_hardware_flags_c_lib",
"android.os.flags-aconfig-cc-test",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
@@ -67,6 +69,7 @@
static_libs: [
"libsurfaceflinger_common",
"libsurfaceflingerflags",
+ "aconfig_hardware_flags_c_lib",
"android.os.flags-aconfig-cc",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
@@ -83,6 +86,7 @@
static_libs: [
"libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
+ "aconfig_hardware_flags_c_lib",
"android.os.flags-aconfig-cc-test",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index e28a0c1..858f759 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -27,6 +27,7 @@
#include <cinttypes>
#include <android_os.h>
+#include <android_hardware_flags.h>
#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <com_android_server_display_feature_flags.h>
@@ -279,5 +280,6 @@
FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(trace_frame_rate_override, "",
com::android::graphics::libgui::flags);
-
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(luts_api, "",
+ android::hardware::flags);
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 1a857c8..05af721 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -102,6 +102,7 @@
bool deprecate_frame_tracker() const;
bool skip_invisible_windows_in_input() const;
bool begone_bright_hlg() const;
+ bool luts_api() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d6a1ad4..fc0fabd 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -31,6 +31,7 @@
description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16"
bug: "356987016"
is_fixed_read_only: true
+ is_exported: true
} # arr_setframerate_api
flag {
@@ -47,6 +48,7 @@
description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16"
bug: "356987016"
is_fixed_read_only: true
+ is_exported: true
} # arr_surfacecontrol_setframerate_api
flag {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 767000e..53a9062 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -584,7 +584,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -623,7 +623,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -654,6 +654,72 @@
EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
}
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_arr) {
+ SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+ // Set the test to be on a vrr mode.
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+ ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive.
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_mrr) {
+ SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+ // True by default on MRR devices as well, but the device is not set to VRR mode.
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, 0);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+ ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive.
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index c976697..3036fec 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,6 +34,8 @@
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
+ MOCK_METHOD(void, onModeRejected, (PhysicalDisplayId displayId, DisplayModeId modeId),
+ (override));
MOCK_METHOD(void, onFrameRateOverridesChanged,
(PhysicalDisplayId, std::vector<FrameRateOverride>), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index d45cc66..25dd68e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -29,7 +29,7 @@
MOCK_METHOD(void, onChoreographerAttached, (), (override));
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
- MOCK_METHOD(void, onCommitNotComposited, (PhysicalDisplayId), (override));
+ MOCK_METHOD(void, onCommitNotComposited, (), (override));
MOCK_METHOD(void, vrrDisplayIdle, (bool), (override));
};
@@ -39,7 +39,7 @@
void kernelTimerChanged(bool) override {}
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
- void onCommitNotComposited(PhysicalDisplayId) override {}
+ void onCommitNotComposited() override {}
void vrrDisplayIdle(bool) override {}
};