Merge "Revert^2 "Mark @FlaggedApi flags as exported"" into main
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/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/display_luts.h b/include/android/display_luts.h
index 08dfb12..eae2bfd 100644
--- a/include/android/display_luts.h
+++ b/include/android/display_luts.h
@@ -43,6 +43,7 @@
enum ADisplayLuts_SamplingKey : int32_t {
ADISPLAYLUTS_SAMPLINGKEY_RGB = 0,
ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB = 1,
+ ADISPLAYLUTS_SAMPLINGKEY_CIE_Y = 2,
};
typedef enum ADisplayLuts_SamplingKey ADisplayLuts_SamplingKey;
@@ -74,7 +75,8 @@
* @return a new \a ADisplayLutsEntry instance.
*/
ADisplayLutsEntry* _Nonnull ADisplayLutsEntry_createEntry(float* _Nonnull buffer,
- int32_t length, int32_t dimension, int32_t key) __INTRODUCED_IN(36);
+ int32_t length, ADisplayLuts_Dimension dimension, ADisplayLuts_SamplingKey key)
+ __INTRODUCED_IN(36);
/**
* Destroy the \a ADisplayLutsEntry instance.
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index fba063d..ca86c27 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -76,6 +76,8 @@
struct APerformanceHintManager;
struct APerformanceHintSession;
struct AWorkDuration;
+struct ANativeWindow;
+struct ASurfaceControl;
/**
* {@link AWorkDuration} is an opaque type that represents the breakdown of the
@@ -150,6 +152,9 @@
*/
typedef struct APerformanceHintSession APerformanceHintSession;
+typedef struct ANativeWindow ANativeWindow;
+typedef struct ASurfaceControl ASurfaceControl;
+
/**
* Acquire an instance of the performance hint manager.
*
@@ -354,6 +359,39 @@
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.
+ *
+ * This method is primarily intended for sessions that manage the timing of an entire
+ * graphics pipeline end-to-end, such as those using the
+ * {@link ASessionCreationConfig_setGraphicsPipeline} API. However, any session directly
+ * or indirectly managing a graphics pipeline should still associate themselves with
+ * directly relevant ASurfaceControl or ANativeWindow instances for better optimization.
+ *
+ * To see any benefit from this method, the client must make sure they are updating the framerate
+ * of attached surfaces using methods such as {@link ANativeWindow_setFrameRate}, or by updating
+ * any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
+ *
+ * @param session The {@link APerformanceHintSession} instance to update.
+ * @param nativeWindows A pointer to a list of ANativeWindows associated with this session.
+ * nullptr can be passed to indicate there are no associated ANativeWindows.
+ * @param nativeWindowsSize The number of ANativeWindows in the list.
+ * @param surfaceControls A pointer to a list of ASurfaceControls associated with this session.
+ * nullptr can be passed to indicate there are no associated ASurfaceControls.
+ * @param surfaceControlsSize The number of ASurfaceControls in the list.
+ *
+ * @return 0 on success.
+ * EPIPE if communication has failed.
+ * ENOTSUP if unsupported.
+ * EINVAL if invalid or empty arguments passed.
+ */
+
+int APerformanceHint_setNativeSurfaces(APerformanceHintSession* _Nonnull session,
+ ANativeWindow* _Nonnull* _Nullable nativeWindows, int nativeWindowsSize,
+ ASurfaceControl* _Nonnull* _Nullable surfaceControls, int surfaceControlsSize)
+ __INTRODUCED_IN(36);
+
+/**
* Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should
* call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources
* associated with it.
@@ -520,6 +558,63 @@
int ASessionCreationConfig_setGraphicsPipeline(
ASessionCreationConfig* _Nonnull config, bool enabled) __INTRODUCED_IN(36);
+/**
+ * Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
+ * instances managed by this session. See {@link APerformanceHint_setNativeSurfaces}
+ * for more details.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param nativeWindows A pointer to a list of ANativeWindows associated with this session.
+ * nullptr can be passed to indicate there are no associated ANativeWindows.
+ * @param nativeWindowsSize The number of ANativeWindows in the list.
+ * @param surfaceControls A pointer to a list of ASurfaceControls associated with this session.
+ * nullptr can be passed to indicate there are no associated ASurfaceControls.
+ * @param surfaceControlsSize The number of ASurfaceControls in the list.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported.
+ * EINVAL if invalid or empty arguments passed.
+ */
+int ASessionCreationConfig_setNativeSurfaces(
+ ASessionCreationConfig* _Nonnull config,
+ ANativeWindow* _Nonnull* _Nullable nativeWindows, int nativeWindowsSize,
+ ASurfaceControl* _Nonnull* _Nullable surfaceControls, int surfaceControlsSize)
+ __INTRODUCED_IN(36);
+
+/**
+ * Enable automatic timing mode for sessions using the GRAPHICS_PIPELINE API with an attached
+ * surface. In this mode, sessions do not need to report actual durations and only need
+ * to keep their thread list up-to-date, set a native surface, call
+ * {@link ASessionCreationConfig_setGraphicsPipeline()} to signal that the session is in
+ * "graphics pipeline" mode, and then set whether automatic timing is desired for the
+ * CPU, GPU, or both, using this method.
+ *
+ * It is still be beneficial to set an accurate target time, as this may help determine
+ * timing information for some workloads where there is less information available from
+ * the framework, such as games. Additionally, reported CPU durations will be ignored
+ * while automatic CPU timing is enabled, and similarly GPU durations will be ignored
+ * when automatic GPU timing is enabled. When both are enabled, the entire
+ * reportActualWorkDuration call will be ignored, and the session will be managed
+ * completely automatically.
+ *
+ * This mode will not work unless the client makes sure they are updating the framerate
+ * of attached surfaces with methods such as {@link ANativeWindow_setFrameRate}, or updating
+ * any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param cpu Whether to enable automatic timing for the CPU for this session.
+ * @param gpu Whether to enable automatic timing for the GPU for this session.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported.
+ */
+int ASessionCreationConfig_setUseAutoTiming(
+ ASessionCreationConfig* _Nonnull config,
+ bool cpu, bool gpu)
+ __INTRODUCED_IN(36);
+
__END_DECLS
#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9554015..6323333 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -781,69 +781,6 @@
__INTRODUCED_IN(31);
/**
- * Sets the intended frame rate for the given \a surface_control.
- *
- * On devices that are capable of running the display at different frame rates,
- * the system may choose a display refresh rate to better match this surface's frame
- * rate. Usage of this API won't introduce frame rate throttling, or affect other
- * aspects of the application's frame production pipeline. However, because the system
- * may change the display refresh rate, calls to this function may result in changes
- * to Choreographer callback timings, and changes to the time interval at which the
- * system releases buffers back to the application.
- *
- * You can register for changes in the refresh rate using
- * \a AChoreographer_registerRefreshRateCallback.
- *
- * See ASurfaceTransaction_clearFrameRate().
- *
- * Available since API level 36.
- *
- * \param desiredMinRate The desired minimum frame rate (inclusive) for the surface, specifying that
- * the surface prefers the device render rate to be at least `desiredMinRate`.
- *
- * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate.
- *
- * <p>Set `desiredMinRate` = 0 to indicate the surface has no preference
- * and any frame rate is acceptable.
- *
- * <p>The value should be greater than or equal to 0.
- *
- * \param desiredMaxRate The desired maximum frame rate (inclusive) for the surface, specifying that
- * the surface prefers the device render rate to be at most `desiredMaxRate`.
- *
- * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate.
- *
- * <p>Set `desiredMaxRate` = positive infinity to indicate the surface has no preference
- * and any frame rate is acceptable.
- *
- * <p>The value should be greater than or equal to `desiredMinRate`.
- *
- * \param fixedSourceRate The "fixed source" frame rate of the surface if the content has an
- * inherently fixed frame rate, e.g. a video that has a specific frame rate.
- *
- * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a
- * multiple, the surface can render without frame pulldown, for optimal smoothness. For
- * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps,
- * 60 fps, 90 fps, 120 fps, and so on.
- *
- * <p>Setting the fixed source rate can also be used together with a desired
- * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still
- * means the surface's content has a fixed frame rate of `fixedSourceRate`, but additionally
- * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an
- * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth
- * animation on the same surface which looks good when drawing within a frame rate range such as
- * [`desiredMinRate`, `desiredMaxRate`] = [60,120].
- *
- * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface
- * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such
- * as a black screen for a second or two.
- */
-void ASurfaceTransaction_setFrameRateParams(
- ASurfaceTransaction* _Nonnull transaction, ASurfaceControl* _Nonnull surface_control,
- float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
- ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36);
-
-/**
* Clears the frame rate which is set for \a surface_control.
*
* This is equivalent to calling
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/private/display_luts_private.h b/include/private/display_luts_private.h
index 240e1f9..c347a0c 100644
--- a/include/private/display_luts_private.h
+++ b/include/private/display_luts_private.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/display_luts.h>
#include <stdint.h>
#include <vector>
#include <utils/RefBase.h>
@@ -29,9 +30,9 @@
};
struct ADisplayLutsEntry_properties {
- int32_t dimension;
+ ADisplayLuts_Dimension dimension;
int32_t size;
- int32_t samplingKey;
+ ADisplayLuts_SamplingKey samplingKey;
};
struct ADisplayLutsEntry: public RefBase {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 0a61178..e10e4dd 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -477,9 +477,34 @@
},
}
+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",
+ ],
srcs: [
"BufferedTextOutput.cpp",
"BackendUnifiedServiceManager.cpp",
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index d32eecd..123dfd2 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -30,6 +30,12 @@
constexpr bool kUseCache = false;
#endif
+#ifdef LIBBINDER_ADDSERVICE_CACHE
+constexpr bool kUseCacheInAddService = true;
+#else
+constexpr bool kUseCacheInAddService = false;
+#endif
+
using AidlServiceManager = android::os::IServiceManager;
using android::os::IAccessor;
using binder::Status;
@@ -125,31 +131,36 @@
if (!kUseCache) {
return Status::ok();
}
+
+ if (service.getTag() == os::Service::Tag::binder) {
+ return updateCache(serviceName, service.get<os::Service::Tag::binder>());
+ }
+ return Status::ok();
+}
+
+Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
+ const sp<IBinder>& binder) {
std::string traceStr;
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");
+ } 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");
}
return Status::ok();
}
@@ -277,7 +288,12 @@
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);
+ }
+ 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..b46bf19 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,20 @@
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 returnIfCached(const std::string& serviceName, os::Service* _out);
};
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 39d8c24..61866d7 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
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/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/tests/Android.bp b/libs/binder/tests/Android.bp
index 28a3f65..642fbf3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -70,7 +70,10 @@
static_libs: [
"libfakeservicemanager",
],
- defaults: ["libbinder_client_cache_flag"],
+ defaults: [
+ "libbinder_client_cache_flag",
+ "libbinder_addservice_cache_flag",
+ ],
test_suites: ["general-tests"],
require_root: true,
}
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index c5ad793..3195c55 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -34,6 +34,12 @@
constexpr bool kUseLibbinderCache = false;
#endif
+#ifdef LIBBINDER_ADDSERVICE_CACHE
+constexpr bool kUseCacheInAddService = true;
+#else
+constexpr bool kUseCacheInAddService = false;
+#endif
+
// A service name which is in the static list of cachable services
const String16 kCachedServiceName = String16("isub");
@@ -74,14 +80,58 @@
innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority));
}
+ void clearServices() { innerSm.clear(); }
+
FakeServiceManager innerSm;
};
+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 +158,7 @@
}
}
+ sp<MockAidlServiceManager> fakeServiceManager;
sp<android::IServiceManager> mServiceManager;
};
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 0e84d68..dcda1ee 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -75,7 +75,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);
}
diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
index 04bfb28..5d3287d 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.
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 5ce4076..f5a1db5 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -265,16 +265,6 @@
return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
-int32_t ANativeWindow_setFrameRateParams(
- ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
- ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate_params(window, desiredMinRate, desiredMaxRate,
- fixedSourceRate, changeFrameRateStrategy);
-}
-
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index bd8d67a..ed3e8c1 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -33,8 +33,8 @@
#ifndef ANDROID_NATIVE_WINDOW_H
#define ANDROID_NATIVE_WINDOW_H
-#include <stdbool.h>
#include <stdint.h>
+#include <stdbool.h>
#include <sys/cdefs.h>
#include <android/data_space.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
};
/**
@@ -282,7 +288,7 @@
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
-typedef enum ANativeWindow_ChangeFrameRateStrategy : int8_t {
+enum ANativeWindow_ChangeFrameRateStrategy {
/**
* Change the frame rate only if the transition is going to be seamless.
*/
@@ -292,7 +298,7 @@
* i.e. with visual interruptions for the user.
*/
ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
-} ANativeWindow_ChangeFrameRateStrategy __INTRODUCED_IN(31);
+} __INTRODUCED_IN(31);
/**
* Sets the intended frame rate for this window.
@@ -345,76 +351,6 @@
__INTRODUCED_IN(31);
/**
- * Sets the intended frame rate for this window.
- *
- * On devices that are capable of running the display at different frame rates,
- * the system may choose a display refresh rate to better match this surface's frame
- * rate. Usage of this API won't introduce frame rate throttling, or affect other
- * aspects of the application's frame production pipeline. However, because the system
- * may change the display refresh rate, calls to this function may result in changes
- * to Choreographer callback timings, and changes to the time interval at which the
- * system releases buffers back to the application.
- *
- * Note that this only has an effect for surfaces presented on the display. If this
- * surface is consumed by something other than the system compositor, e.g. a media
- * codec, this call has no effect.
- *
- * You can register for changes in the refresh rate using
- * \a AChoreographer_registerRefreshRateCallback.
- *
- * See ANativeWindow_clearFrameRate().
- *
- * Available since API level 36.
- *
- * \param window pointer to an ANativeWindow object.
- *
- * \param desiredMinRate The desired minimum frame rate (inclusive) for the window, specifying that
- * the surface prefers the device render rate to be at least `desiredMinRate`.
- *
- * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate.
- *
- * <p>Set `desiredMinRate` = 0 to indicate the window has no preference
- * and any frame rate is acceptable.
- *
- * <p>The value should be greater than or equal to 0.
- *
- * \param desiredMaxRate The desired maximum frame rate (inclusive) for the window, specifying that
- * the surface prefers the device render rate to be at most `desiredMaxRate`.
- *
- * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate.
- *
- * <p>Set `desiredMaxRate` = positive infinity to indicate the window has no preference
- * and any frame rate is acceptable.
- *
- * <p>The value should be greater than or equal to `desiredMinRate`.
- *
- * \param fixedSourceRate The "fixed source" frame rate of the window if the content has an
- * inherently fixed frame rate, e.g. a video that has a specific frame rate.
- *
- * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a
- * multiple, the surface can render without frame pulldown, for optimal smoothness. For
- * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps,
- * 60 fps, 90 fps, 120 fps, and so on.
- *
- * <p>Setting the fixed source rate can also be used together with a desired
- * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still
- * means the window's content has a fixed frame rate of `fixedSourceRate`, but additionally
- * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an
- * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth
- * animation on the same window which looks good when drawing within a frame rate range such as
- * [`desiredMinRate`, `desiredMaxRate`] = [60,120].
- *
- * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface
- * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such
- * as a black screen for a second or two.
- *
- * \return 0 for success, -EINVAL if the arguments are invalid.
- */
-int32_t ANativeWindow_setFrameRateParams(
- ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
- ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36);
-
-/**
* Clears the frame rate which is set for this window.
*
* This is equivalent to calling
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 05f49ad..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
};
/*
@@ -1156,23 +1151,6 @@
(int)compatibility, (int)changeFrameRateStrategy);
}
-static inline int native_window_set_frame_rate_params(struct ANativeWindow* window,
- float desiredMinRate, float desiredMaxRate,
- float fixedSourceRate,
- int8_t changeFrameRateStrategy) {
- // TODO(b/362798998): Fix plumbing to send whole params
- int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
- : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
- double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
- ? fixedSourceRate
- : desiredMinRate;
- if (desiredMaxRate < desiredMinRate) {
- return -EINVAL;
- }
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, frameRate, compatibility,
- changeFrameRateStrategy);
-}
-
struct ANativeWindowFrameTimelineInfo {
// Frame Id received from ANativeWindow_getNextFrameId.
uint64_t frameNumber;
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 071e354..e29d5a6 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -53,7 +53,6 @@
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # systemapi introduced=30
ANativeWindow_setFrameRate; # introduced=30
- ANativeWindow_setFrameRateParams; # introduced=36
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
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/opengl/libs/Android.bp b/opengl/libs/Android.bp
index b19a862..91250b9 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -63,6 +63,18 @@
unversioned_until: "current",
}
+aconfig_declarations {
+ name: "egl_flags",
+ package: "com.android.graphics.egl.flags",
+ container: "system",
+ srcs: ["EGL/egl_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "libegl_flags",
+ aconfig_declarations: "egl_flags",
+}
+
cc_defaults {
name: "gl_libs_defaults",
cflags: [
@@ -136,6 +148,7 @@
],
export_include_dirs: ["EGL"],
shared_libs: [
+ "libegl_flags",
"libz",
],
}
@@ -166,6 +179,7 @@
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
"libbase",
+ "libegl_flags",
"libhidlbase",
"libnativebridge_lazy",
"libnativeloader_lazy",
@@ -202,6 +216,7 @@
"EGL/MultifileBlobCache_test.cpp",
],
shared_libs: [
+ "libegl_flags",
"libutils",
"libz",
],
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index f7e33b3..04c525e 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -38,11 +38,20 @@
#include <utils/JenkinsHash.h>
+#include <com_android_graphics_egl_flags.h>
+
+using namespace com::android::graphics::egl;
+
using namespace std::literals;
constexpr uint32_t kMultifileMagic = 'MFB$';
constexpr uint32_t kCrcPlaceholder = 0;
+// When removing files, what fraction of the overall limit should be reached when removing files
+// A divisor of two will decrease the cache to 50%, four to 25% and so on
+// We use the same limit to manage size and entry count
+constexpr uint32_t kCacheLimitDivisor = 2;
+
namespace {
// Helper function to close entries or free them
@@ -72,6 +81,7 @@
mMaxTotalEntries(maxTotalEntries),
mTotalCacheSize(0),
mTotalCacheEntries(0),
+ mTotalCacheSizeDivisor(kCacheLimitDivisor),
mHotCacheLimit(0),
mHotCacheSize(0),
mWorkerThreadIdle(true) {
@@ -80,8 +90,13 @@
return;
}
- // Set the cache version, override if debug value set
+ // Set the cache version
mCacheVersion = kMultifileBlobCacheVersion;
+ // Bump the version if we're using flagged features
+ if (flags::multifile_blobcache_advanced_usage()) {
+ mCacheVersion++;
+ }
+ // Override if debug value set
int debugCacheVersion = base::GetIntProperty("debug.egl.blobcache.cache_version", -1);
if (debugCacheVersion >= 0) {
ALOGV("INIT: Using %u as cacheVersion instead of %u", debugCacheVersion, mCacheVersion);
@@ -122,7 +137,7 @@
// Check that our cacheVersion and buildId match
struct stat st;
if (stat(mMultifileDirName.c_str(), &st) == 0) {
- if (checkStatus(mMultifileDirName.c_str())) {
+ if (checkStatus(mMultifileDirName)) {
statusGood = true;
} else {
ALOGV("INIT: Cache status has changed, clearing the cache");
@@ -237,11 +252,9 @@
ALOGV("INIT: Entry %u is good, tracking it now.", entryHash);
- // Track details for rapid lookup later
- trackEntry(entryHash, header.valueSize, fileSize, st.st_atime);
-
- // Track the total size
- increaseTotalCacheSize(fileSize);
+ // Track details for rapid lookup later and update total size
+ // Note access time is a full timespec instead of just seconds
+ trackEntry(entryHash, header.valueSize, fileSize, st.st_atim);
// Preload the entry for fast retrieval
if ((mHotCacheSize + fileSize) < mHotCacheLimit) {
@@ -317,6 +330,28 @@
// Generate a hash of the key and use it to track this entry
uint32_t entryHash = android::JenkinsHashMixBytes(0, static_cast<const uint8_t*>(key), keySize);
+ std::string fullPath = mMultifileDirName + "/" + std::to_string(entryHash);
+
+ // See if we already have this file
+ if (flags::multifile_blobcache_advanced_usage() && contains(entryHash)) {
+ // Remove previous entry from hot cache
+ removeFromHotCache(entryHash);
+
+ // Remove previous entry and update the overall cache size
+ removeEntry(entryHash);
+
+ // If valueSize is zero, this is an indication that the user wants to remove the entry from
+ // cache It has already been removed from tracking, now remove it from disk It is safe to do
+ // this immediately because we drained the write queue in removeFromHotCache
+ if (valueSize == 0) {
+ ALOGV("SET: Zero size detected for existing entry, removing %u from cache", entryHash);
+ if (remove(fullPath.c_str()) != 0) {
+ ALOGW("SET: Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ }
+ return;
+ }
+ }
+
size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize;
// If we're going to be over the cache limit, kick off a trim to clear space
@@ -339,13 +374,12 @@
memcpy(static_cast<void*>(buffer + sizeof(MultifileHeader) + keySize),
static_cast<const void*>(value), valueSize);
- std::string fullPath = mMultifileDirName + "/" + std::to_string(entryHash);
-
- // Track the size and access time for quick recall
- trackEntry(entryHash, valueSize, fileSize, time(0));
-
- // Update the overall cache size
- increaseTotalCacheSize(fileSize);
+ // Track the size and access time for quick recall and update the overall cache size
+ struct timespec time = {0, 0};
+ if (flags::multifile_blobcache_advanced_usage()) {
+ clock_gettime(CLOCK_REALTIME, &time);
+ }
+ trackEntry(entryHash, valueSize, fileSize, time);
// Keep the entry in hot cache for quick retrieval
ALOGV("SET: Adding %u to hot cache.", entryHash);
@@ -427,6 +461,14 @@
if (mHotCache.find(entryHash) != mHotCache.end()) {
ALOGV("GET: HotCache HIT for entry %u", entryHash);
cacheEntry = mHotCache[entryHash].entryBuffer;
+
+ if (flags::multifile_blobcache_advanced_usage()) {
+ // Update last access time on disk
+ struct timespec times[2];
+ times[0].tv_nsec = UTIME_NOW;
+ times[1].tv_nsec = UTIME_OMIT;
+ utimensat(0, fullPath.c_str(), times, 0);
+ }
} else {
ALOGV("GET: HotCache MISS for entry: %u", entryHash);
@@ -455,6 +497,14 @@
cacheEntry =
reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
+ if (flags::multifile_blobcache_advanced_usage()) {
+ // Update last access time and omit last modify time
+ struct timespec times[2];
+ times[0].tv_nsec = UTIME_NOW;
+ times[1].tv_nsec = UTIME_OMIT;
+ futimens(fd, times);
+ }
+
// We can close the file now and the mmap will remain
close(fd);
@@ -491,6 +541,13 @@
return 0;
}
+ if (flags::multifile_blobcache_advanced_usage()) {
+ // Update the entry time for this hash, so it reflects LRU
+ struct timespec time;
+ clock_gettime(CLOCK_REALTIME, &time);
+ updateEntryTime(entryHash, time);
+ }
+
// Remaining entry following the key is the value
uint8_t* cachedValue = cacheEntry + (keySize + sizeof(MultifileHeader));
memcpy(value, cachedValue, cachedValueSize);
@@ -626,9 +683,47 @@
}
void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
- time_t accessTime) {
+ const timespec& accessTime) {
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+ // When we add this entry to the map, it is sorted by accessTime
+ MultifileEntryStatsMapIter entryStatsIter =
+ mEntryStats.emplace(std::piecewise_construct, std::forward_as_tuple(accessTime),
+ std::forward_as_tuple(entryHash, valueSize, fileSize));
+
+ // Track all entries with quick access to its stats
+ mEntries.emplace(entryHash, entryStatsIter);
+#else
+ (void)accessTime;
mEntries.insert(entryHash);
- mEntryStats[entryHash] = {valueSize, fileSize, accessTime};
+ mEntryStats[entryHash] = {entryHash, valueSize, fileSize};
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+
+ increaseTotalCacheSize(fileSize);
+}
+
+bool MultifileBlobCache::removeEntry(uint32_t entryHash) {
+ auto entryIter = mEntries.find(entryHash);
+ if (entryIter == mEntries.end()) {
+ return false;
+ }
+
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+ MultifileEntryStatsMapIter entryStatsIter = entryIter->second;
+ MultifileEntryStats entryStats = entryStatsIter->second;
+ decreaseTotalCacheSize(entryStats.fileSize);
+#else
+ auto entryStatsIter = mEntryStats.find(entryHash);
+ if (entryStatsIter == mEntryStats.end()) {
+ ALOGE("Failed to remove entryHash (%u) from mEntryStats", entryHash);
+ return false;
+ }
+ decreaseTotalCacheSize(entryStatsIter->second.fileSize);
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+
+ mEntryStats.erase(entryStatsIter);
+ mEntries.erase(entryIter);
+
+ return true;
}
bool MultifileBlobCache::contains(uint32_t hashEntry) const {
@@ -636,7 +731,40 @@
}
MultifileEntryStats MultifileBlobCache::getEntryStats(uint32_t entryHash) {
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+ auto entryIter = mEntries.find(entryHash);
+ if (entryIter == mEntries.end()) {
+ return {};
+ }
+
+ MultifileEntryStatsMapIter entryStatsIter = entryIter->second;
+ MultifileEntryStats entryStats = entryStatsIter->second;
+ return entryStats;
+#else
return mEntryStats[entryHash];
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+}
+
+void MultifileBlobCache::updateEntryTime(uint32_t entryHash, const timespec& newTime) {
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+ // This function updates the ordering of the map by removing the old iterators
+ // and re-adding them. If should be perforant as it does not perform a full re-sort.
+ // First, pull out the old entryStats
+ auto entryIter = mEntries.find(entryHash);
+ MultifileEntryStatsMapIter entryStatsIter = entryIter->second;
+ MultifileEntryStats entryStats = std::move(entryStatsIter->second);
+
+ // Remove the old iterators
+ mEntryStats.erase(entryStatsIter);
+ mEntries.erase(entryIter);
+
+ // Insert the new with updated time
+ entryStatsIter = mEntryStats.emplace(std::make_pair(newTime, std::move(entryStats)));
+ mEntries.emplace(entryHash, entryStatsIter);
+#else
+ (void)entryHash;
+ (void)newTime;
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
}
void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) {
@@ -718,31 +846,32 @@
bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) {
// Walk through our map of sorted last access times and remove files until under the limit
for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) {
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+ const MultifileEntryStats& entryStats = cacheEntryIter->second;
+ uint32_t entryHash = entryStats.entryHash;
+#else
uint32_t entryHash = cacheEntryIter->first;
+ const MultifileEntryStats& entryStats = cacheEntryIter->second;
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
ALOGV("LRU: Removing entryHash %u", entryHash);
- // Track the overall size
- MultifileEntryStats entryStats = getEntryStats(entryHash);
- decreaseTotalCacheSize(entryStats.fileSize);
-
// Remove it from hot cache if present
removeFromHotCache(entryHash);
// Remove it from the system
std::string entryPath = mMultifileDirName + "/" + std::to_string(entryHash);
if (remove(entryPath.c_str()) != 0) {
- ALOGE("LRU: Error removing %s: %s", entryPath.c_str(), std::strerror(errno));
- return false;
+ // Continue evicting invalid item (app's cache might be cleared)
+ ALOGW("LRU: Error removing %s: %s", entryPath.c_str(), std::strerror(errno));
}
// Increment the iterator before clearing the entry
cacheEntryIter++;
- // Delete the entry from our tracking
- size_t count = mEntryStats.erase(entryHash);
- if (count != 1) {
- ALOGE("LRU: Failed to remove entryHash (%u) from mEntryStats", entryHash);
+ // Delete the entry from our tracking and update the overall cache size
+ if (!removeEntry(entryHash)) {
+ ALOGE("LRU: Failed to remove entryHash %u", entryHash);
return false;
}
@@ -794,11 +923,6 @@
return true;
}
-// When removing files, what fraction of the overall limit should be reached when removing files
-// A divisor of two will decrease the cache to 50%, four to 25% and so on
-// We use the same limit to manage size and entry count
-constexpr uint32_t kCacheLimitDivisor = 2;
-
// Calculate the cache size and remove old entries until under the limit
void MultifileBlobCache::trimCache() {
// Wait for all deferred writes to complete
@@ -806,8 +930,10 @@
waitForWorkComplete();
ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu",
- mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor);
- if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor)) {
+ mMaxTotalSize / mTotalCacheSizeDivisor, mMaxTotalEntries / mTotalCacheSizeDivisor);
+
+ if (!applyLRU(mMaxTotalSize / mTotalCacheSizeDivisor,
+ mMaxTotalEntries / mTotalCacheSizeDivisor)) {
ALOGE("Error when clearing multifile shader cache");
return;
}
@@ -830,9 +956,36 @@
// Create the file or reset it if already present, read+write for user only
int fd = open(fullPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
- ALOGE("Cache error in SET - failed to open fullPath: %s, error: %s",
- fullPath.c_str(), std::strerror(errno));
- return;
+ if (flags::multifile_blobcache_advanced_usage()) {
+ struct stat st;
+ if (stat(mMultifileDirName.c_str(), &st) == -1) {
+ ALOGW("Cache directory missing (app's cache cleared?). Recreating...");
+
+ // Restore the multifile directory
+ if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) {
+ ALOGE("Cache error in SET - Unable to create directory (%s), errno "
+ "(%i)",
+ mMultifileDirName.c_str(), errno);
+ return;
+ }
+
+ // Create new status file
+ if (!createStatus(mMultifileDirName.c_str())) {
+ ALOGE("Cache error in SET - Failed to create status file!");
+ return;
+ }
+
+ // Try to open the file again
+ fd = open(fullPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ }
+ }
+
+ if (fd == -1) {
+ ALOGE("Cache error in SET - failed to open fullPath: %s, error: %s",
+ fullPath.c_str(), std::strerror(errno));
+ return;
+ }
}
ALOGV("DEFERRED: Opened fd %i from %s", fd, fullPath.c_str());
@@ -849,6 +1002,14 @@
return;
}
+ if (flags::multifile_blobcache_advanced_usage()) {
+ // Update last access time and last modify time
+ struct timespec times[2];
+ times[0].tv_nsec = UTIME_NOW;
+ times[1].tv_nsec = UTIME_NOW;
+ futimens(fd, times);
+ }
+
ALOGV("DEFERRED: Completed write for: %s", fullPath.c_str());
close(fd);
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index 65aa2db..3bd393f 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -32,6 +32,10 @@
#include "FileBlobCache.h"
+#include <com_android_graphics_egl_flags.h>
+
+using namespace com::android::graphics::egl;
+
namespace android {
constexpr uint32_t kMultifileBlobCacheVersion = 2;
@@ -45,9 +49,9 @@
};
struct MultifileEntryStats {
+ uint32_t entryHash;
EGLsizeiANDROID valueSize;
size_t fileSize;
- time_t accessTime;
};
struct MultifileStatus {
@@ -100,6 +104,26 @@
size_t mBufferSize;
};
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+struct MultifileTimeLess {
+ bool operator()(const struct timespec& t1, const struct timespec& t2) const {
+ if (t1.tv_sec == t2.tv_sec) {
+ // If seconds are equal, check nanoseconds
+ return t1.tv_nsec < t2.tv_nsec;
+ } else {
+ // Otherwise, compare seconds
+ return t1.tv_sec < t2.tv_sec;
+ }
+ }
+};
+
+// The third parameter here causes all entries to be sorted by access time,
+// so oldest will be accessed first in applyLRU
+using MultifileEntryStatsMap =
+ std::multimap<struct timespec, MultifileEntryStats, MultifileTimeLess>;
+using MultifileEntryStatsMapIter = MultifileEntryStatsMap::iterator;
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+
class MultifileBlobCache {
public:
MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
@@ -115,6 +139,7 @@
size_t getTotalSize() const { return mTotalCacheSize; }
size_t getTotalEntries() const { return mTotalCacheEntries; }
+ size_t getTotalCacheSizeDivisor() const { return mTotalCacheSizeDivisor; }
const std::string& getCurrentBuildId() const { return mBuildId; }
void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; }
@@ -124,10 +149,11 @@
private:
void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
- time_t accessTime);
+ const timespec& accessTime);
bool contains(uint32_t entryHash) const;
bool removeEntry(uint32_t entryHash);
MultifileEntryStats getEntryStats(uint32_t entryHash);
+ void updateEntryTime(uint32_t entryHash, const timespec& newTime);
bool createStatus(const std::string& baseDir);
bool checkStatus(const std::string& baseDir);
@@ -151,8 +177,14 @@
std::string mBuildId;
uint32_t mCacheVersion;
+#if COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+ std::unordered_map<uint32_t, MultifileEntryStatsMapIter> mEntries;
+ MultifileEntryStatsMap mEntryStats;
+#else
std::unordered_set<uint32_t> mEntries;
std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats;
+#endif // COM_ANDROID_GRAPHICS_EGL_FLAGS(MULTIFILE_BLOBCACHE_ADVANCED_USAGE)
+
std::unordered_map<uint32_t, MultifileHotCache> mHotCache;
size_t mMaxKeySize;
@@ -161,6 +193,7 @@
size_t mMaxTotalEntries;
size_t mTotalCacheSize;
size_t mTotalCacheEntries;
+ size_t mTotalCacheSizeDivisor;
size_t mHotCacheLimit;
size_t mHotCacheEntryLimit;
size_t mHotCacheSize;
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index 90a0f1e..85fb29e 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -21,10 +21,15 @@
#include <fcntl.h>
#include <gtest/gtest.h>
#include <stdio.h>
+#include <utils/JenkinsHash.h>
#include <fstream>
#include <memory>
+#include <com_android_graphics_egl_flags.h>
+
+using namespace com::android::graphics::egl;
+
using namespace std::literals;
namespace android {
@@ -55,6 +60,7 @@
std::vector<std::string> getCacheEntries();
void clearProperties();
+ bool clearCache();
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
@@ -314,7 +320,7 @@
struct stat info;
if (stat(multifileDirName.c_str(), &info) == 0) {
- // We have a multifile dir. Skip the status file and return the only entry.
+ // We have a multifile dir. Skip the status file and return the entries.
DIR* dir;
struct dirent* entry;
if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
@@ -325,6 +331,7 @@
if (strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
+ // printf("Found entry: %s\n", entry->d_name);
cacheEntries.push_back(multifileDirName + "/" + entry->d_name);
}
} else {
@@ -458,6 +465,8 @@
// Set one entry
mMBC->set("abcd", 4, "efgh", 4);
+ uint32_t initialCacheVersion = mMBC->getCurrentCacheVersion();
+
// Close the cache so everything writes out
mMBC->finish();
mMBC.reset();
@@ -466,7 +475,7 @@
ASSERT_EQ(getCacheEntries().size(), 1);
// Set a debug cacheVersion
- std::string newCacheVersion = std::to_string(kMultifileBlobCacheVersion + 1);
+ std::string newCacheVersion = std::to_string(initialCacheVersion + 1);
ASSERT_TRUE(base::SetProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
ASSERT_TRUE(
base::WaitForProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
@@ -503,4 +512,404 @@
ASSERT_EQ(getCacheEntries().size(), 0);
}
+// Ensure cache is correct when a key is reused
+TEST_F(MultifileBlobCacheTest, SameKeyDifferentValues) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
+
+ size_t startingSize = mMBC->getTotalSize();
+
+ // New cache should be empty
+ ASSERT_EQ(startingSize, 0);
+
+ // Set an initial value
+ mMBC->set("ab", 2, "cdef", 4);
+
+ // Grab the new size
+ size_t firstSize = mMBC->getTotalSize();
+
+ // Ensure the size went up
+ // Note: Checking for an exact size is challenging, as the
+ // file size can differ between platforms.
+ ASSERT_GT(firstSize, startingSize);
+
+ // Verify the cache is correct
+ ASSERT_EQ(size_t(4), mMBC->get("ab", 2, buf, 4));
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_EQ('d', buf[1]);
+ ASSERT_EQ('e', buf[2]);
+ ASSERT_EQ('f', buf[3]);
+
+ // Now reuse the key with a smaller value
+ mMBC->set("ab", 2, "gh", 2);
+
+ // Grab the new size
+ size_t secondSize = mMBC->getTotalSize();
+
+ // Ensure it decreased in size
+ ASSERT_LT(secondSize, firstSize);
+
+ // Verify the cache is correct
+ ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
+ ASSERT_EQ('g', buf[0]);
+ ASSERT_EQ('h', buf[1]);
+
+ // Now put back the original value
+ mMBC->set("ab", 2, "cdef", 4);
+
+ // And we should get back a stable size
+ size_t finalSize = mMBC->getTotalSize();
+ ASSERT_EQ(firstSize, finalSize);
+}
+
+// Ensure cache is correct when a key is reused with large value size
+TEST_F(MultifileBlobCacheTest, SameKeyLargeValues) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ // Create the cache with larger limits to stress test reuse
+ constexpr uint32_t kLocalMaxKeySize = 1 * 1024 * 1024;
+ constexpr uint32_t kLocalMaxValueSize = 4 * 1024 * 1024;
+ constexpr uint32_t kLocalMaxTotalSize = 32 * 1024 * 1024;
+ mMBC.reset(new MultifileBlobCache(kLocalMaxKeySize, kLocalMaxValueSize, kLocalMaxTotalSize,
+ kMaxTotalEntries, &mTempFile->path[0]));
+
+ constexpr uint32_t kLargeValueCount = 8;
+ constexpr uint32_t kLargeValueSize = 64 * 1024;
+
+ // Create a several really large values
+ unsigned char largeValue[kLargeValueCount][kLargeValueSize];
+ for (int i = 0; i < kLargeValueCount; i++) {
+ for (int j = 0; j < kLargeValueSize; j++) {
+ // Fill the value with the index for uniqueness
+ largeValue[i][j] = i;
+ }
+ }
+
+ size_t startingSize = mMBC->getTotalSize();
+
+ // New cache should be empty
+ ASSERT_EQ(startingSize, 0);
+
+ // Cycle through the values and set them all in sequence
+ for (int i = 0; i < kLargeValueCount; i++) {
+ mMBC->set("abcd", 4, largeValue[i], kLargeValueSize);
+ }
+
+ // Ensure we get the last one back
+ unsigned char outBuf[kLargeValueSize];
+ mMBC->get("abcd", 4, outBuf, kLargeValueSize);
+
+ for (int i = 0; i < kLargeValueSize; i++) {
+ // Buffer should contain highest index value
+ ASSERT_EQ(kLargeValueCount - 1, outBuf[i]);
+ }
+}
+
+// Ensure cache eviction is LRU
+TEST_F(MultifileBlobCacheTest, CacheEvictionIsLRU) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ // Fill the cache with exactly how much it can hold
+ int entry = 0;
+ for (entry = 0; entry < kMaxTotalEntries; entry++) {
+ // Use the index as the key and value
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+
+ int result = 0;
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // Ensure the cache is full
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries);
+
+ // Add one more entry to trigger eviction
+ size_t overflowEntry = kMaxTotalEntries;
+ mMBC->set(&overflowEntry, sizeof(overflowEntry), &overflowEntry, sizeof(overflowEntry));
+
+ // Verify it contains the right amount, which will be one more than reduced size
+ // because we evict the cache before adding a new entry
+ size_t evictionLimit = kMaxTotalEntries / mMBC->getTotalCacheSizeDivisor();
+ ASSERT_EQ(mMBC->getTotalEntries(), evictionLimit + 1);
+
+ // Ensure cache is as expected, with old entries removed, newer entries remaining
+ for (entry = 0; entry < kMaxTotalEntries; entry++) {
+ int result = 0;
+ mMBC->get(&entry, sizeof(entry), &result, sizeof(result));
+
+ if (entry < evictionLimit) {
+ // We should get no hits on evicted entries, i.e. the first added
+ ASSERT_EQ(result, 0);
+ } else {
+ // Above the limit should still be present
+ ASSERT_EQ(result, entry);
+ }
+ }
+}
+
+// Ensure calling GET on an entry updates its access time, even if already in hotcache
+TEST_F(MultifileBlobCacheTest, GetUpdatesAccessTime) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ // Fill the cache with exactly how much it can hold
+ int entry = 0;
+ int result = 0;
+ for (entry = 0; entry < kMaxTotalEntries; entry++) {
+ // Use the index as the key and value
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // Ensure the cache is full
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries);
+
+ // GET the first few entries to update their access time
+ std::vector<int> accessedEntries = {1, 2, 3};
+ for (int i = 0; i < accessedEntries.size(); i++) {
+ entry = accessedEntries[i];
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ }
+
+ // Add one more entry to trigger eviction
+ size_t overflowEntry = kMaxTotalEntries;
+ mMBC->set(&overflowEntry, sizeof(overflowEntry), &overflowEntry, sizeof(overflowEntry));
+
+ size_t evictionLimit = kMaxTotalEntries / mMBC->getTotalCacheSizeDivisor();
+
+ // Ensure cache is as expected, with old entries removed, newer entries remaining
+ for (entry = 0; entry < kMaxTotalEntries; entry++) {
+ int result = 0;
+ mMBC->get(&entry, sizeof(entry), &result, sizeof(result));
+
+ if (std::find(accessedEntries.begin(), accessedEntries.end(), entry) !=
+ accessedEntries.end()) {
+ // If this is one of the handful we accessed after filling the cache,
+ // they should still be in the cache because LRU
+ ASSERT_EQ(result, entry);
+ } else if (entry >= (evictionLimit + accessedEntries.size())) {
+ // If they were above the eviction limit (plus three for our updated entries),
+ // they should still be present
+ ASSERT_EQ(result, entry);
+ } else {
+ // Otherwise, they shold be evicted and no longer present
+ ASSERT_EQ(result, 0);
+ }
+ }
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Open the cache again
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Check the cache again, ensuring the updated access time made it to disk
+ for (entry = 0; entry < kMaxTotalEntries; entry++) {
+ int result = 0;
+ mMBC->get(&entry, sizeof(entry), &result, sizeof(result));
+ if (std::find(accessedEntries.begin(), accessedEntries.end(), entry) !=
+ accessedEntries.end()) {
+ ASSERT_EQ(result, entry);
+ } else if (entry >= (evictionLimit + accessedEntries.size())) {
+ ASSERT_EQ(result, entry);
+ } else {
+ ASSERT_EQ(result, 0);
+ }
+ }
+}
+
+bool MultifileBlobCacheTest::clearCache() {
+ std::string cachePath = &mTempFile->path[0];
+ std::string multifileDirName = cachePath + ".multifile";
+
+ DIR* dir = opendir(multifileDirName.c_str());
+ if (dir == nullptr) {
+ printf("Error opening directory: %s\n", multifileDirName.c_str());
+ return false;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir)) != nullptr) {
+ // Skip "." and ".." entries
+ if (std::string(entry->d_name) == "." || std::string(entry->d_name) == "..") {
+ continue;
+ }
+
+ std::string entryPath = multifileDirName + "/" + entry->d_name;
+
+ // Delete the entry (we assert it's a file, nothing nested here)
+ if (unlink(entryPath.c_str()) != 0) {
+ printf("Error deleting file: %s\n", entryPath.c_str());
+ closedir(dir);
+ return false;
+ }
+ }
+
+ closedir(dir);
+
+ // Delete the empty directory itself
+ if (rmdir(multifileDirName.c_str()) != 0) {
+ printf("Error deleting directory %s, error %s\n", multifileDirName.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+// Recover from lost cache in the case of app clearing it
+TEST_F(MultifileBlobCacheTest, RecoverFromLostCache) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ int entry = 0;
+ int result = 0;
+
+ uint32_t kEntryCount = 10;
+
+ // Add some entries
+ for (entry = 0; entry < kEntryCount; entry++) {
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // For testing, wait until the entries have completed writing
+ mMBC->finish();
+
+ // Manually delete the cache!
+ ASSERT_TRUE(clearCache());
+
+ // Cache should not contain any entries
+ for (entry = 0; entry < kEntryCount; entry++) {
+ ASSERT_EQ(size_t(0), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ }
+
+ // Ensure we can still add new ones
+ for (entry = kEntryCount; entry < kEntryCount * 2; entry++) {
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Open the cache again
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Before fixes, writing the second entries to disk should have failed due to missing
+ // cache dir. But now they should have survived our shutdown above.
+ for (entry = kEntryCount; entry < kEntryCount * 2; entry++) {
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+}
+
+// Ensure cache eviction succeeds if the cache is deleted
+TEST_F(MultifileBlobCacheTest, EvictAfterLostCache) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ int entry = 0;
+ int result = 0;
+
+ uint32_t kEntryCount = 10;
+
+ // Add some entries
+ for (entry = 0; entry < kEntryCount; entry++) {
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // For testing, wait until the entries have completed writing
+ mMBC->finish();
+
+ // Manually delete the cache!
+ ASSERT_TRUE(clearCache());
+
+ // Now start adding entries to trigger eviction, cache should survive
+ for (entry = kEntryCount; entry < 2 * kMaxTotalEntries; entry++) {
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // We should have triggered multiple evictions above and remain at or below the
+ // max amount of entries
+ ASSERT_LE(getCacheEntries().size(), kMaxTotalEntries);
+}
+
+// Remove from cache when size is zero
+TEST_F(MultifileBlobCacheTest, ZeroSizeRemovesEntry) {
+ if (!flags::multifile_blobcache_advanced_usage()) {
+ GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
+ }
+
+ // Put some entries in
+ int entry = 0;
+ int result = 0;
+
+ uint32_t kEntryCount = 20;
+
+ // Add some entries
+ for (entry = 0; entry < kEntryCount; entry++) {
+ mMBC->set(&entry, sizeof(entry), &entry, sizeof(entry));
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(entry, result);
+ }
+
+ // Send some of them again with size zero
+ std::vector<int> removedEntries = {5, 10, 18};
+ for (int i = 0; i < removedEntries.size(); i++) {
+ entry = removedEntries[i];
+ mMBC->set(&entry, sizeof(entry), nullptr, 0);
+ }
+
+ // Ensure they do not get a hit
+ for (int i = 0; i < removedEntries.size(); i++) {
+ entry = removedEntries[i];
+ ASSERT_EQ(size_t(0), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ }
+
+ // And have been removed from disk
+ std::vector<std::string> diskEntries = getCacheEntries();
+ ASSERT_EQ(diskEntries.size(), kEntryCount - removedEntries.size());
+ for (int i = 0; i < removedEntries.size(); i++) {
+ entry = removedEntries[i];
+ // Generate a hash for our removed entries and ensure they are not contained
+ // Note our entry and key and the same here, so we're hashing the key just like
+ // the multifile blobcache does.
+ uint32_t entryHash =
+ android::JenkinsHashMixBytes(0, reinterpret_cast<uint8_t*>(&entry), sizeof(entry));
+ ASSERT_EQ(std::find(diskEntries.begin(), diskEntries.end(), std::to_string(entryHash)),
+ diskEntries.end());
+ }
+
+ // Ensure the others are still present
+ for (entry = 0; entry < kEntryCount; entry++) {
+ if (std::find(removedEntries.begin(), removedEntries.end(), entry) ==
+ removedEntries.end()) {
+ ASSERT_EQ(sizeof(entry), mMBC->get(&entry, sizeof(entry), &result, sizeof(result)));
+ ASSERT_EQ(result, entry);
+ }
+ }
+}
+
} // namespace android
diff --git a/opengl/libs/EGL/egl_flags.aconfig b/opengl/libs/EGL/egl_flags.aconfig
new file mode 100644
index 0000000..1157970
--- /dev/null
+++ b/opengl/libs/EGL/egl_flags.aconfig
@@ -0,0 +1,13 @@
+package: "com.android.graphics.egl.flags"
+container: "system"
+
+flag {
+ name: "multifile_blobcache_advanced_usage"
+ namespace: "gpu"
+ description: "This flag controls new behaviors to address bugs found via advanced usage"
+ bug: "380483358"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/opengl/libs/EGL/fuzzer/Android.bp b/opengl/libs/EGL/fuzzer/Android.bp
index 4947e5f..fe5f2a6 100644
--- a/opengl/libs/EGL/fuzzer/Android.bp
+++ b/opengl/libs/EGL/fuzzer/Android.bp
@@ -37,6 +37,7 @@
],
shared_libs: [
+ "libegl_flags",
"libz",
],
diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp
index aebd3f2..ed46efd 100644
--- a/opengl/tests/EGLTest/Android.bp
+++ b/opengl/tests/EGLTest/Android.bp
@@ -26,6 +26,7 @@
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
"libEGL",
+ "libegl_flags",
"libbase",
"libcutils",
"libbinder",
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 38e5974..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;
}
@@ -205,20 +204,30 @@
}
NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
- std::scoped_lock _l(getLock());
+ NotifyMotionArgs newArgs(args);
+ PointerDisplayChange pointerDisplayChange;
+ { // acquire lock
+ std::scoped_lock _l(getLock());
+ if (isFromMouse(args)) {
+ newArgs = processMouseEventLocked(args);
+ pointerDisplayChange = calculatePointerDisplayChangeToNotify();
+ } else if (isFromTouchpad(args)) {
+ newArgs = processTouchpadEventLocked(args);
+ pointerDisplayChange = calculatePointerDisplayChangeToNotify();
+ } else if (isFromDrawingTablet(args)) {
+ processDrawingTabletEventLocked(args);
+ } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+ processStylusHoverEventLocked(args);
+ } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+ processTouchscreenAndStylusEventLocked(args);
+ }
+ } // release lock
- if (isFromMouse(args)) {
- return processMouseEventLocked(args);
- } else if (isFromTouchpad(args)) {
- return processTouchpadEventLocked(args);
- } else if (isFromDrawingTablet(args)) {
- processDrawingTabletEventLocked(args);
- } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
- processStylusHoverEventLocked(args);
- } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
- processTouchscreenAndStylusEventLocked(args);
+ if (pointerDisplayChange) {
+ // pointer display may have changed if mouse crossed display boundary
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
- return args;
+ return newArgs;
}
NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
@@ -235,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);
@@ -245,7 +254,8 @@
// This is a relative mouse, so move the cursor by the specified amount.
processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
}
- if (canUnfadeOnDisplay(displayId)) {
+ // Note displayId may have changed if the cursor moved to a different display
+ if (canUnfadeOnDisplay(newArgs.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
return newArgs;
@@ -262,17 +272,19 @@
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;
}
- if (canUnfadeOnDisplay(displayId)) {
+
+ // Note displayId may have changed if the cursor moved to a different display
+ if (canUnfadeOnDisplay(newArgs.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
return newArgs;
@@ -283,12 +295,104 @@
const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- pc.move(deltaX, deltaY);
- 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;
+ 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);
+ // pointer may have moved to a different viewport
+ newArgs.displayId = pc.getDisplayId();
+ }
+
+ 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 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);
+ 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.
+ // In practise, majority of cases we only have non-zero values in either x or y coordinates,
+ // except sometimes near the corners.
+ // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
+ // the destination display for the same reason.
+ DisplayPosition sourceBoundary;
+ float cursorOffset = 0.0f;
+ if (rotatedUnconsumedDelta.x > 0) {
+ sourceBoundary = DisplayPosition::RIGHT;
+ cursorOffset = rotatedCursorPosition.y;
+ } else if (rotatedUnconsumedDelta.x < 0) {
+ sourceBoundary = DisplayPosition::LEFT;
+ cursorOffset = rotatedCursorPosition.y;
+ } else if (rotatedUnconsumedDelta.y > 0) {
+ sourceBoundary = DisplayPosition::BOTTOM;
+ cursorOffset = rotatedCursorPosition.x;
+ } else {
+ sourceBoundary = DisplayPosition::TOP;
+ cursorOffset = rotatedCursorPosition.x;
+ }
+
+ const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId();
+ std::optional<std::pair<const DisplayViewport*, float /*offset*/>> destination =
+ findDestinationDisplayLocked(sourceDisplayId, sourceBoundary, cursorOffset);
+ if (!destination.has_value()) {
+ // No matching adjacent display
+ return;
+ }
+
+ const DisplayViewport& destinationViewport = *destination->first;
+ const float destinationOffset = destination->second;
+ if (mMousePointersByDisplay.find(destinationViewport.displayId) !=
+ mMousePointersByDisplay.end()) {
+ LOG(FATAL) << "A cursor already exists on destination display"
+ << destinationViewport.displayId;
+ }
+ mDefaultMouseDisplayId = destinationViewport.displayId;
+ auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
+ pcNode.key() = destinationViewport.displayId;
+ mMousePointersByDisplay.insert(std::move(pcNode));
+
+ // Before updating the viewport and moving the cursor to appropriate location in the destination
+ // viewport, we need to temporarily hide the cursor. This will prevent it from appearing at the
+ // center of the display in any intermediate frames.
+ pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
+ pc.setDisplayViewport(destinationViewport);
+ vec2 destinationPosition =
+ calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset,
+ sourceBoundary);
+
+ // Transform position back to un-rotated coordinate space before sending it to controller
+ destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
+ destinationPosition.y);
+ pc.setPosition(destinationPosition.x, destinationPosition.y);
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+}
+
+vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport,
+ float pointerOffset,
+ DisplayPosition sourceBoundary) {
+ // destination is opposite of the source boundary
+ switch (sourceBoundary) {
+ case DisplayPosition::RIGHT:
+ return {0, pointerOffset}; // left edge
+ case DisplayPosition::TOP:
+ return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
+ case DisplayPosition::LEFT:
+ return {destinationViewport.logicalRight, pointerOffset}; // right edge
+ case DisplayPosition::BOTTOM:
+ return {pointerOffset, 0}; // top edge
+ }
}
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
@@ -436,7 +540,8 @@
}
void PointerChoreographer::onControllerAddedOrRemovedLocked() {
- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
+ if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() &&
+ !com::android::input::flags::connected_displays_cursor()) {
return;
}
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
@@ -502,6 +607,13 @@
mNextListener.notify(args);
}
+void PointerChoreographer::setDisplayTopology(
+ const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
+ displayTopology) {
+ std::scoped_lock _l(getLock());
+ mTopology = displayTopology;
+}
+
void PointerChoreographer::dump(std::string& dump) {
std::scoped_lock _l(getLock());
@@ -654,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;
@@ -725,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);
@@ -873,6 +985,97 @@
return ConstructorDelegate(std::move(ctor));
}
+void PointerChoreographer::populateFakeDisplayTopologyLocked(
+ const std::vector<gui::DisplayInfo>& displayInfos) {
+ if (!com::android::input::flags::connected_displays_cursor()) {
+ return;
+ }
+
+ if (displayInfos.size() == mTopology.size()) {
+ bool displaysChanged = false;
+ for (const auto& displayInfo : displayInfos) {
+ if (mTopology.find(displayInfo.displayId) == mTopology.end()) {
+ displaysChanged = true;
+ break;
+ }
+ }
+
+ if (!displaysChanged) {
+ return;
+ }
+ }
+
+ // create a fake topology assuming following order
+ // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
+ // This also adds a 100px offset on corresponding edge for better manual testing
+ // ┌────────┐
+ // │ next ├─────────┐
+ // ┌─└───────┐┤ next 2 │ ...
+ // │ default │└─────────┘
+ // └─────────┘
+ mTopology.clear();
+
+ // treat default display as base, in real topology it should be the primary-display
+ ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
+ for (const auto& displayInfo : displayInfos) {
+ if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) {
+ continue;
+ }
+ if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
+ mTopology[previousDisplay].push_back(
+ {displayInfo.displayId, DisplayPosition::TOP, 100});
+ mTopology[displayInfo.displayId].push_back(
+ {previousDisplay, DisplayPosition::BOTTOM, -100});
+ } else {
+ mTopology[previousDisplay].push_back(
+ {displayInfo.displayId, DisplayPosition::RIGHT, 100});
+ mTopology[displayInfo.displayId].push_back(
+ {previousDisplay, DisplayPosition::LEFT, -100});
+ }
+ previousDisplay = displayInfo.displayId;
+ }
+
+ // update default pointer display. In real topology it should be the primary-display
+ if (mTopology.find(mDefaultMouseDisplayId) == mTopology.end()) {
+ mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT;
+ }
+}
+
+std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
+PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
+ const DisplayPosition sourceBoundary,
+ float cursorOffset) const {
+ const auto& sourceNode = mTopology.find(sourceDisplayId);
+ if (sourceNode == mTopology.end()) {
+ // Topology is likely out of sync with viewport info, wait for it to be updated
+ LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
+ return std::nullopt;
+ }
+ for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) {
+ if (adjacentDisplay.position != sourceBoundary) {
+ continue;
+ }
+ const DisplayViewport* destinationViewport =
+ findViewportByIdLocked(adjacentDisplay.displayId);
+ if (destinationViewport == nullptr) {
+ // Topology is likely out of sync with viewport info, wait for them to be updated
+ LOG(WARNING) << "Cannot find viewport for adjacent display "
+ << adjacentDisplay.displayId << "of source display " << sourceDisplayId;
+ continue;
+ }
+ // target position must be within target display boundary
+ const int32_t edgeSize =
+ sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM
+ ? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
+ : (destinationViewport->logicalBottom - destinationViewport->logicalTop);
+ if (cursorOffset >= adjacentDisplay.offsetPx &&
+ cursorOffset <= adjacentDisplay.offsetPx + edgeSize) {
+ return std::make_pair(destinationViewport, adjacentDisplay.offsetPx);
+ }
+ }
+ return std::nullopt;
+}
+
// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
@@ -883,12 +1086,14 @@
}
auto newPrivacySensitiveDisplays =
getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
+
+ // PointerChoreographer uses Listener's lock.
+ base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
- // PointerChoreographer uses Listener's lock.
- base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
}
+ mPointerChoreographer->populateFakeDisplayTopologyLocked(windowInfosUpdate.displayInfos);
}
void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index fba1aef..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,
@@ -113,11 +113,29 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ // TODO(b/362719483) remove these when real topology is available
+ enum class DisplayPosition {
+ RIGHT,
+ TOP,
+ LEFT,
+ BOTTOM,
+ ftl_last = BOTTOM,
+ };
+
+ struct AdjacentDisplay {
+ ui::LogicalDisplayId displayId;
+ DisplayPosition position;
+ float offsetPx;
+ };
+ void setDisplayTopology(
+ const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
+ displayTopology);
+
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
@@ -153,6 +171,22 @@
const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
REQUIRES(getLock());
+ void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
+ REQUIRES(getLock());
+
+ void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+ REQUIRES(getLock());
+
+ std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
+ const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary,
+ float cursorOffset) const REQUIRES(getLock());
+
+ static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport,
+ float pointerOffset, DisplayPosition sourceBoundary);
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology
+ GUARDED_BY(getLock());
+
/* This listener keeps tracks of visible privacy sensitive displays and updates the
* choreographer if there are any changes.
*
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 10fec74..43eaf67 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -35,109 +35,34 @@
]
},
{
- "name": "CtsHardwareTestCases",
- "options": [
- {
- "include-filter": "android.hardware.input.cts.tests"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "CtsHardwareTestCases_cts_tests"
},
{
"name": "CtsInputTestCases"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.input"
- }
- ]
+ "name": "CtsViewTestCases_cts_input"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.HoverTest"
- },
- {
- "include-filter": "android.view.cts.MotionEventTest"
- },
- {
- "include-filter": "android.view.cts.PointerCaptureTest"
- },
- {
- "include-filter": "android.view.cts.TooltipTest"
- },
- {
- "include-filter": "android.view.cts.TouchDelegateTest"
- },
- {
- "include-filter": "android.view.cts.VerifyInputEventTest"
- },
- {
- "include-filter": "android.view.cts.ViewTest"
- },
- {
- "include-filter": "android.view.cts.ViewUnbufferedTest"
- }
- ]
+ "name": "CtsViewTestCases_input_related"
},
{
- "name": "CtsWidgetTestCases",
- "options": [
- {
- "include-filter": "android.widget.cts.NumberPickerTest"
- },
- {
- "include-filter": "android.widget.cts.SeekBarTest"
- }
- ]
+ "name": "CtsWidgetTestCases_seekbar_and_numberpicker"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.hardware.input"
- }
- ]
+ "name": "FrameworksCoreTests_hardware_input"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.VerifiedKeyEventTest"
- },
- {
- "include-filter": "android.view.VerifiedMotionEventTest"
- }
- ]
+ "name": "FrameworksCoreTests_view_verified"
},
{
- "name": "CtsAppTestCases",
- "options": [
- {
- "include-filter": "android.app.cts.ToolbarActionBarTest"
- }
- ]
+ "name": "CtsAppTestCases_cts_toolbaractionbartest"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.input"
- }
- ]
+ "name": "FrameworksServicesTests_android_server_input"
},
{
- "name": "CtsSecurityTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.MotionEventTest"
- }
- ]
+ "name": "CtsSecurityTestCases_cts_motioneventtest"
},
{
"name": "CtsSecurityBulletinHostTestCases",
@@ -156,12 +81,7 @@
],
"postsubmit": [
{
- "name": "CtsWindowManagerDeviceWindow",
- "options": [
- {
- "include-filter": "android.server.wm.window.WindowInputTests"
- }
- ]
+ "name": "CtsWindowManagerDeviceWindow_window_windowinputtests"
},
{
"name": "libinput_tests"
@@ -187,98 +107,31 @@
]
},
{
- "name": "CtsHardwareTestCases",
- "options": [
- {
- "include-filter": "android.hardware.input.cts.tests"
- }
- ]
+ "name": "CtsHardwareTestCases_cts_tests"
},
{
"name": "CtsInputTestCases"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.input"
- }
- ]
+ "name": "CtsViewTestCases_cts_input"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.HoverTest"
- },
- {
- "include-filter": "android.view.cts.MotionEventTest"
- },
- {
- "include-filter": "android.view.cts.PointerCaptureTest"
- },
- {
- "include-filter": "android.view.cts.TooltipTest"
- },
- {
- "include-filter": "android.view.cts.TouchDelegateTest"
- },
- {
- "include-filter": "android.view.cts.VerifyInputEventTest"
- },
- {
- "include-filter": "android.view.cts.ViewTest"
- },
- {
- "include-filter": "android.view.cts.ViewUnbufferedTest"
- }
- ]
+ "name": "CtsViewTestCases_input_related"
},
{
- "name": "CtsWidgetTestCases",
- "options": [
- {
- "include-filter": "android.widget.cts.NumberPickerTest"
- },
- {
- "include-filter": "android.widget.cts.SeekBarTest"
- }
- ]
+ "name": "CtsWidgetTestCases_seekbar_and_numberpicker"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.VerifiedKeyEventTest"
- },
- {
- "include-filter": "android.view.VerifiedMotionEventTest"
- }
- ]
+ "name": "FrameworksCoreTests_view_verified"
},
{
- "name": "CtsAppTestCases",
- "options": [
- {
- "include-filter": "android.app.cts.ToolbarActionBarTest"
- }
- ]
+ "name": "CtsAppTestCases_cts_toolbaractionbartest"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.input"
- }
- ]
+ "name": "FrameworksServicesTests_server_input"
},
{
- "name": "CtsSecurityTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.MotionEventTest"
- }
- ]
+ "name": "CtsSecurityTestCases_cts_motioneventtest"
},
{
"name": "CtsSecurityBulletinHostTestCases",
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
index 2c5fe21..4dd6073 100644
--- a/services/inputflinger/dispatcher/EventLogTags.logtags
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
# 62000 - 62199 reserved for inputflinger
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 580cde3..4d6b6c7 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -245,6 +245,9 @@
// True to use three-finger tap as a customizable shortcut; false to use it as a middle-click.
bool touchpadThreeFingerTapShortcutEnabled;
+ // True to enable system gestures (three- and four-finger swipes) on touchpads.
+ bool touchpadSystemGesturesEnabled;
+
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
@@ -297,6 +300,7 @@
shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
touchpadThreeFingerTapShortcutEnabled(false),
+ touchpadSystemGesturesEnabled(true),
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false),
mouseReverseVerticalScrollingEnabled(false),
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 8f3d9ca..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.
*
@@ -72,14 +58,18 @@
/* Dumps the state of the pointer controller. */
virtual std::string dump() = 0;
- /* Move the pointer. */
- virtual void move(float deltaX, float deltaY) = 0;
+ /* Move the pointer and return unconsumed delta if the pointer has crossed the current
+ * viewport bounds.
+ *
+ * Return value may be used to move pointer to corresponding adjacent display, if it exists in
+ * the display-topology */
+ [[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.
@@ -145,6 +135,8 @@
/* Resets the flag to skip screenshot of the pointer indicators for all displays. */
virtual void clearSkipScreenshotFlags() = 0;
+
+ virtual ui::Transform getDisplayTransform() const = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 9eeb2b2..7434ae4 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -47,10 +47,6 @@
return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
}
-static inline bool isKeyboardBacklightCustomLevelsEnabled() {
- return sysprop::InputProperties::enable_keyboard_backlight_custom_levels().value_or(true);
-}
-
/**
* Input controller owned by InputReader device, implements the native API for querying input
* lights, getting and setting the lights brightness and color, by interacting with EventHub
@@ -289,8 +285,7 @@
std::set<BrightnessLevel> PeripheralController::getPreferredBrightnessLevels(
const Light* light) const {
std::set<BrightnessLevel> levels;
- if (!isKeyboardBacklightCustomLevelsEnabled() ||
- light->type != InputDeviceLightType::KEYBOARD_BACKLIGHT) {
+ if (light->type != InputDeviceLightType::KEYBOARD_BACKLIGHT) {
return levels;
}
std::optional<std::string> keyboardBacklightLevels =
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index ca8266b..0c094e6 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -351,6 +351,7 @@
bumpGeneration();
}
+ std::list<NotifyArgs> out;
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
.setBoolValues({true});
@@ -375,11 +376,11 @@
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState;
-
mGestureConverter.setThreeFingerTapShortcutEnabled(
config.touchpadThreeFingerTapShortcutEnabled);
+ out += mGestureConverter.setEnableSystemGestures(when,
+ config.touchpadSystemGesturesEnabled);
}
- std::list<NotifyArgs> out;
if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
mPointerCaptured = config.pointerCaptureRequest.isEnable();
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 54270eb..6bd949a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -132,6 +132,15 @@
return out;
}
+std::list<NotifyArgs> GestureConverter::setEnableSystemGestures(nsecs_t when, bool enable) {
+ std::list<NotifyArgs> out;
+ if (!enable && mCurrentClassification == MotionClassification::MULTI_FINGER_SWIPE) {
+ out += handleMultiFingerSwipeLift(when, when);
+ }
+ mEnableSystemGestures = enable;
+ return out;
+}
+
void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
@@ -461,6 +470,9 @@
uint32_t fingerCount,
float dx, float dy) {
std::list<NotifyArgs> out = {};
+ if (!mEnableSystemGestures) {
+ return out;
+ }
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
// If the user changes the number of fingers mid-way through a swipe (e.g. they start with
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index ad40721..8d92ead 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -59,6 +59,8 @@
mThreeFingerTapShortcutEnabled = enabled;
}
+ [[nodiscard]] std::list<NotifyArgs> setEnableSystemGestures(nsecs_t when, bool enable);
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -104,6 +106,7 @@
InputReaderContext& mReaderContext;
const bool mEnableFlingStop;
const bool mEnableNoFocusChange;
+ bool mEnableSystemGestures{true};
bool mThreeFingerTapShortcutEnabled;
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 7a1d0ec..3c7f432 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -47,6 +47,7 @@
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
data: Data,
+ down_key_map: HashMap<i32, HashSet<i32>>,
}
#[derive(Default)]
@@ -69,15 +70,34 @@
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
) -> StickyKeysFilter {
- Self { next, listener, data: Default::default() }
+ Self { next, listener, data: Default::default(), down_key_map: HashMap::new() }
}
}
impl Filter for StickyKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
+ let down = event.action == KeyEventAction::DOWN;
let up = event.action == KeyEventAction::UP;
let mut modifier_state = self.data.modifier_state;
let mut locked_modifier_state = self.data.locked_modifier_state;
+ if down {
+ let down_keys = self.down_key_map.entry(event.deviceId).or_default();
+ down_keys.insert(event.keyCode);
+ } else {
+ if !self.down_key_map.contains_key(&event.deviceId) {
+ self.next.notify_key(event);
+ return;
+ }
+ let down_keys = self.down_key_map.get_mut(&event.deviceId).unwrap();
+ if !down_keys.contains(&event.keyCode) {
+ self.next.notify_key(event);
+ return;
+ }
+ down_keys.remove(&event.keyCode);
+ if down_keys.is_empty() {
+ self.down_key_map.remove(&event.deviceId);
+ }
+ }
if !is_ephemeral_modifier_key(event.keyCode) {
// If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
// CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
@@ -130,6 +150,7 @@
self.data.locked_modifier_state = ModifierState::None;
self.listener.modifier_state_changed(ModifierState::None, ModifierState::None);
}
+ self.down_key_map.retain(|key, _| device_infos.iter().any(|x| *key == x.deviceId));
self.next.notify_devices_changed(device_infos);
}
@@ -166,6 +187,7 @@
result += &format!("\tmodifier_state = {:?}\n", self.data.modifier_state);
result += &format!("\tlocked_modifier_state = {:?}\n", self.data.locked_modifier_state);
result += &format!("\tcontributing_devices = {:?}\n", self.data.contributing_devices);
+ result += &format!("\tdown_key_map = {:?}\n", self.down_key_map);
self.next.dump(dump_str + &result)
}
}
@@ -322,6 +344,31 @@
}
#[test]
+ fn test_notify_key_passes_ephemeral_modifier_keys_if_only_key_up_occurs() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_ALT_LEFT,
+ KEYCODE_ALT_RIGHT,
+ KEYCODE_CTRL_LEFT,
+ KEYCODE_CTRL_RIGHT,
+ KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT,
+ KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT,
+ ];
+ for key_code in key_codes.iter() {
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+ }
+
+ #[test]
fn test_notify_key_passes_non_ephemeral_modifier_keys() {
let test_filter = TestFilter::new();
let test_callbacks = TestCallbacks::new();
@@ -437,6 +484,26 @@
}
#[test]
+ fn test_modifier_state_unchanged_on_non_modifier_key_up_without_down() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
+ }
+
+ #[test]
fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() {
let test_filter = TestFilter::new();
let test_callbacks = TestCallbacks::new();
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 887a939..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,15 +148,20 @@
return mIsPointerShown;
}
-void FakePointerController::move(float deltaX, float deltaY) {
- if (!mEnabled) return;
+vec2 FakePointerController::move(float deltaX, float deltaY) {
+ if (!mEnabled) return {0, 0};
mX += deltaX;
+ mY += deltaY;
+
+ const vec2 position(mX, mY);
+
if (mX < mMinX) mX = mMinX;
if (mX > mMaxX) mX = mMaxX;
- mY += deltaY;
if (mY < mMinY) mY = mMinY;
if (mY > mMaxY) mY = mMaxY;
+
+ return {position.x - mX, position.y - mY};
}
void FakePointerController::fade(Transition) {
@@ -190,4 +195,8 @@
mSpotsByDisplay.clear();
}
+ui::Transform FakePointerController::getDisplayTransform() const {
+ return ui::Transform();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 9b773a7..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;
@@ -48,6 +48,7 @@
void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override;
void clearSkipScreenshotFlags() override;
void fade(Transition) override;
+ ui::Transform getDisplayTransform() const override;
void assertViewportSet(ui::LogicalDisplayId displayId);
void assertViewportNotSet();
@@ -65,7 +66,7 @@
private:
std::string dump() override { return ""; }
- void 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/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index fad8f05..fe40a5e 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -48,6 +48,7 @@
using testing::AllOf;
using testing::Each;
using testing::ElementsAre;
+using testing::IsEmpty;
using testing::VariantWith;
class GestureConverterTest : public testing::Test {
@@ -849,6 +850,107 @@
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
+TEST_F(GestureConverterTest, DisablingSystemGestures_IgnoresMultiFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+ std::list<NotifyArgs> args = converter.setEnableSystemGestures(ARBITRARY_TIME, false);
+ ASSERT_THAT(args, IsEmpty());
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/5);
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+
+ args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = converter.setEnableSystemGestures(ARBITRARY_TIME, true);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE))));
+}
+
+TEST_F(GestureConverterTest, DisablingSystemGestures_EndsOngoingMultiFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ std::list<NotifyArgs> args;
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_FALSE(args.empty());
+
+ // Disabling system gestures should end the swipe early.
+ args = converter.setEnableSystemGestures(ARBITRARY_TIME, false);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithMotionClassification(MotionClassification::NONE)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+
+ // Further movement in the same swipe should be ignored.
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_THAT(args, IsEmpty());
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args, IsEmpty());
+
+ // But single-finger pointer motion should be reported.
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithRelativeMotion(-5, 10), WithButtonState(0)))));
+}
+
TEST_F(GestureConverterTest, Pinch_Inwards) {
input_flags::enable_touchpad_no_focus_change(true);
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 411c7ba..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;
});
}
@@ -2601,6 +2601,173 @@
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
}
+using PointerChoreographerDisplayTopologyTestFixtureParam =
+ std::tuple<std::string_view /*name*/, int32_t /*source device*/,
+ ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
+ vec2 /*source position*/, vec2 /*hover move X/Y*/,
+ ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>;
+
+class PointerChoreographerDisplayTopologyTestFixture
+ : public PointerChoreographerTest,
+ public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> {
+public:
+ static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
+ static constexpr ui::LogicalDisplayId DISPLAY_RIGHT_ID = ui::LogicalDisplayId{30};
+ static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
+ static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
+
+ PointerChoreographerDisplayTopologyTestFixture() {
+ com::android::input::flags::connected_displays_cursor(true);
+ }
+
+protected:
+ std::vector<DisplayViewport> mViewports{
+ createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0),
+ createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0),
+ createViewport(DISPLAY_RIGHT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_90),
+ createViewport(DISPLAY_BOTTOM_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_180),
+ createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270),
+ createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90,
+ ui::ROTATION_0),
+ };
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>>
+ mTopology{
+ {DISPLAY_CENTER_ID,
+ {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f},
+ {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f},
+ {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f},
+ {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f},
+ {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT,
+ -90.0f}}},
+ };
+
+private:
+ DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
+ ui::Rotation orientation) {
+ DisplayViewport viewport;
+ viewport.displayId = displayId;
+ viewport.logicalRight = width;
+ viewport.logicalBottom = height;
+ viewport.orientation = orientation;
+ return viewport;
+ }
+};
+
+TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) {
+ const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove,
+ destinationDisplay, destinationPosition] = GetParam();
+
+ mChoreographer.setDisplayViewports(mViewports);
+ mChoreographer.setDefaultMouseDisplayId(
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID);
+ mChoreographer.setDisplayTopology(mTopology);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}});
+
+ auto pc = assertPointerControllerCreated(pointerControllerType);
+ ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(initialPosition.x, initialPosition.y);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ auto pointerBuilder = PointerBuilder(/*id=*/0, pointerToolType)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, hoverMove.x)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, hoverMove.y);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, device)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ ASSERT_TRUE(pc->isPointerShown());
+ ASSERT_EQ(pc->getDisplayId(), destinationDisplay);
+ auto position = pc->getPosition();
+ ASSERT_EQ(position.x, destinationPosition.x);
+ ASSERT_EQ(position.y, destinationPosition.y);
+
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(destinationPosition.x, destinationPosition.y),
+ WithDisplayId(destinationDisplay),
+ WithCursorPosition(destinationPosition.x, destinationPosition.y)));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture,
+ testing::Values(
+ // 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, vec2(50, 50) /* initial x/y */,
+ vec2(25, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ 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, vec2(50, 50) /* initial x/y */,
+ vec2(-100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID,
+ 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,
+ vec2(5, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ vec2(5, 0) /* Top edge */),
+ std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ vec2(99, 5) /* Top edge */),
+ std::make_tuple("NoTransitionAtBottomOffset",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, vec2(5, 95) /* initial x/y */,
+ vec2(0, 100) /* Move Down */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ vec2(5, 99) /* Bottom edge */),
+ std::make_tuple("NoTransitionAtLeftOffset",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, vec2(5, 5) /* initial x/y */,
+ vec2(-100, 0) /* Move Left */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ vec2(0, 5) /* Left edge */),
+ std::make_tuple(
+ "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ 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,
+ vec2(0, 90) /* bottom left corner */)),
+ [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
TEST_F_WITH_FLAGS(
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 71d3d1f..682d1f4 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -69,10 +69,6 @@
(const CpuHeadroomParams& params, CpuHeadroomResult* headroom), (override));
MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroom,
(const GpuHeadroomParams& params, GpuHeadroomResult* headroom), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* interval),
- (override));
- MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* interval),
- (override));
MOCK_METHOD(ndk::ScopedAStatus, sendCompositionData,
(const std::vector<CompositionData>& in_data), (override));
MOCK_METHOD(ndk::ScopedAStatus, sendCompositionUpdate,
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/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 143c192..a040c88 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -589,30 +589,28 @@
void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
- Luts luts;
- // if outputIndependentState.luts is nullptr, it means we want to clear the LUTs
- // and we pass an empty Luts object to the HWC.
- if (outputIndependentState.luts) {
- auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
- auto lutOffsets = outputIndependentState.luts->offsets;
- auto& lutProperties = outputIndependentState.luts->lutProperties;
-
- std::vector<LutProperties> aidlProperties;
- aidlProperties.reserve(lutProperties.size());
- for (size_t i = 0; i < lutOffsets.size(); i++) {
- LutProperties properties;
- properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension);
- properties.size = lutProperties[i].size;
- properties.samplingKeys = {
- static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)};
- aidlProperties.emplace_back(properties);
- }
-
-
- luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
- luts.offsets = lutOffsets;
- luts.lutProperties = std::move(aidlProperties);
+ if (!outputIndependentState.luts) {
+ return;
}
+ auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
+ auto lutOffsets = outputIndependentState.luts->offsets;
+ auto& lutProperties = outputIndependentState.luts->lutProperties;
+
+ std::vector<LutProperties> aidlProperties;
+ aidlProperties.reserve(lutProperties.size());
+ for (size_t i = 0; i < lutOffsets.size(); i++) {
+ LutProperties properties;
+ properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension);
+ properties.size = lutProperties[i].size;
+ properties.samplingKeys = {
+ static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)};
+ aidlProperties.emplace_back(properties);
+ }
+
+ Luts luts;
+ luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
+ luts.offsets = lutOffsets;
+ luts.lutProperties = std::move(aidlProperties);
switch (auto error = hwcLayer->setLuts(luts)) {
case hal::Error::NONE:
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 034c768..dbffe80 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -901,7 +901,6 @@
EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setBlockingRegion(RegionEq(blockingRegion)))
.WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setLuts(_)).WillOnce(Return(kError));
}
void expectSetCompositionTypeCall(Composition compositionType) {
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index f8b6c6e..a086aee 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -28,6 +28,7 @@
#include <ftl/concat.h>
#include <ftl/expected.h>
#include <log/log.h>
+#include <utils/Errors.h>
namespace android::display {
@@ -177,12 +178,13 @@
}
}
-bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
- DisplayModeRequest&& desiredMode,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline& outTimeline) {
+auto DisplayModeController::initiateModeChange(
+ PhysicalDisplayId displayId, DisplayModeRequest&& desiredMode,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline& outTimeline) -> ModeChangeResult {
std::lock_guard lock(mDisplayLock);
- const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(ModeChangeResult::Aborted)).get();
// TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
// For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
@@ -201,13 +203,17 @@
const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
- if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
- &outTimeline) != OK) {
- return false;
+ const auto error = mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(),
+ constraints, &outTimeline);
+ switch (error) {
+ case FAILED_TRANSACTION:
+ return ModeChangeResult::Rejected;
+ case OK:
+ SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+ return ModeChangeResult::Changed;
+ default:
+ return ModeChangeResult::Aborted;
}
-
- SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
- return true;
}
void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 9ec603d..af3e909 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -70,6 +70,7 @@
RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+ enum class ModeChangeResult { Changed, Rejected, Aborted };
DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
EXCLUDES(mDisplayLock);
@@ -86,9 +87,9 @@
scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
- bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
- const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline& outTimeline)
+ ModeChangeResult initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline& outTimeline)
REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
new file mode 100644
index 0000000..c68020c
--- /dev/null
+++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <optional>
+#include <string>
+
+#include <ui/DisplayId.h>
+
+#include "Utils/Dumper.h"
+
+namespace android::display {
+
+// Immutable state of a virtual display, captured on creation.
+class VirtualDisplaySnapshot {
+public:
+ VirtualDisplaySnapshot(GpuVirtualDisplayId gpuId, std::string uniqueId)
+ : mIsGpu(true), mUniqueId(std::move(uniqueId)), mVirtualId(gpuId) {}
+ VirtualDisplaySnapshot(HalVirtualDisplayId halId, std::string uniqueId)
+ : mIsGpu(false), mUniqueId(std::move(uniqueId)), mVirtualId(halId) {}
+
+ VirtualDisplayId displayId() const { return mVirtualId; }
+ bool isGpu() const { return mIsGpu; }
+
+ void dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
+
+ dumper.dump("isGpu"sv, mIsGpu ? "true"sv : "false"sv);
+ dumper.dump("uniqueId"sv, mUniqueId);
+ }
+
+private:
+ const bool mIsGpu;
+ const std::string mUniqueId;
+ const VirtualDisplayId mVirtualId;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 4c8ff58..25f6513 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -37,10 +37,6 @@
namespace android {
-using hardware::hidl_handle;
-using hardware::hidl_vec;
-using hardware::Return;
-
using aidl::android::hardware::graphics::composer3::BnComposerCallback;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
@@ -524,11 +520,15 @@
Error AidlComposer::getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
const auto status =
mAidlComposerClient->getDisplayAttribute(translate<int64_t>(display),
translate<int32_t>(config),
static_cast<AidlDisplayAttribute>(attribute),
outValue);
+#pragma clang diagnostic pop
+
if (!status.isOk()) {
ALOGE("getDisplayAttribute failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
@@ -538,8 +538,13 @@
Error AidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
std::vector<int32_t> configs;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
const auto status =
mAidlComposerClient->getDisplayConfigs(translate<int64_t>(display), &configs);
+#pragma clang diagnostic pop
+
if (!status.isOk()) {
ALOGE("getDisplayConfigs failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
@@ -1384,7 +1389,7 @@
return V2_4::Error::NONE;
}
-V2_4::Error AidlComposer::setActiveConfigWithConstraints(
+Error AidlComposer::setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) {
@@ -1398,10 +1403,10 @@
&timeline);
if (!status.isOk()) {
ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
- return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ return static_cast<Error>(status.getServiceSpecificError());
}
*outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
- return V2_4::Error::NONE;
+ return Error::NONE;
}
V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 933e8d1..6b5ebc5 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -27,11 +27,6 @@
#include <utility>
#include <vector>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
@@ -43,9 +38,6 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
namespace android::Hwc2 {
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
@@ -206,7 +198,7 @@
V2_4::Error getDisplayConnectionType(Display display,
IComposerClient::DisplayConnectionType* outType) override;
V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
- V2_4::Error setActiveConfigWithConstraints(
+ Error setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c1333c2..ff292fa 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,8 @@
#include <ui/PictureProfileHandle.h>
#include <utils/StrongPointer.h>
+#include "DisplayHardware/Hal.h"
+
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
@@ -73,9 +75,9 @@
using types::V1_2::Dataspace;
using types::V1_2::PixelFormat;
+using hardware::graphics::composer::hal::Error;
using V2_1::Config;
using V2_1::Display;
-using V2_1::Error;
using V2_1::Layer;
using V2_4::CommandReaderBase;
using V2_4::CommandWriterBase;
@@ -261,7 +263,7 @@
Display display, IComposerClient::DisplayConnectionType* outType) = 0;
virtual V2_4::Error getDisplayVsyncPeriod(Display display,
VsyncPeriodNanos* outVsyncPeriod) = 0;
- virtual V2_4::Error setActiveConfigWithConstraints(
+ virtual Error setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 224f50e..e90b5b7 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -31,10 +31,11 @@
#include <common/FlagManager.h>
#include <scheduler/Fps.h>
-#include "DisplayHardware/Hal.h"
+#include "Hal.h"
namespace android {
+using aidl::android::hardware::graphics::composer3::OutputType;
namespace hal = android::hardware::graphics::composer::hal;
class DisplayMode;
@@ -114,6 +115,11 @@
return *this;
}
+ Builder& setHdrOutputType(OutputType type) {
+ mDisplayMode->mHdrOutputType = type;
+ return *this;
+ }
+
private:
float getDefaultDensity() {
// Default density is based on TVs: 1080p displays get XHIGH density, lower-
@@ -166,6 +172,8 @@
// without visual interruptions such as a black screen.
int32_t getGroup() const { return mGroup; }
+ OutputType getHdrOutputType() const { return mHdrOutputType; }
+
private:
explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
@@ -179,21 +187,25 @@
Dpi mDpi;
int32_t mGroup = -1;
std::optional<hal::VrrConfig> mVrrConfig;
+ OutputType mHdrOutputType;
};
inline bool equalsExceptDisplayModeId(const DisplayMode& lhs, const DisplayMode& rhs) {
return lhs.getHwcId() == rhs.getHwcId() && lhs.getResolution() == rhs.getResolution() &&
lhs.getVsyncRate().getPeriodNsecs() == rhs.getVsyncRate().getPeriodNsecs() &&
- lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup();
+ lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup() &&
+ lhs.getVrrConfig() == rhs.getVrrConfig() &&
+ lhs.getHdrOutputType() == rhs.getHdrOutputType();
}
inline std::string to_string(const DisplayMode& mode) {
return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, "
- "dpi=%.2fx%.2f, group=%d, vrrConfig=%s}",
+ "dpi=%.2fx%.2f, group=%d, vrrConfig=%s, supportedHdrTypes=%s}",
ftl::to_underlying(mode.getId()), mode.getHwcId(), mode.getWidth(),
mode.getHeight(), to_string(mode.getVsyncRate()).c_str(),
mode.getDpi().x, mode.getDpi().y, mode.getGroup(),
- to_string(mode.getVrrConfig()).c_str());
+ to_string(mode.getVrrConfig()).c_str(),
+ toString(mode.getHdrOutputType()).c_str());
}
template <typename... DisplayModePtrs>
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index a274995..081f4aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -33,6 +33,8 @@
#include <ui/GraphicBuffer.h>
#include <ui/PictureProfileHandle.h>
+#include "DisplayHardware/Hal.h"
+
#include <algorithm>
#include <cinttypes>
#include <iterator>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 61d4541..55ccdef 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -27,6 +27,7 @@
#include "HWComposer.h"
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
#include <android-base/properties.h>
#include <common/trace.h>
#include <compositionengine/Output.h>
@@ -335,7 +336,8 @@
.height = config.height,
.vsyncPeriod = config.vsyncPeriod,
.configGroup = config.configGroup,
- .vrrConfig = config.vrrConfig};
+ .vrrConfig = config.vrrConfig,
+ .hdrOutputType = config.hdrOutputType};
const DisplayConfiguration::Dpi estimatedDPI =
getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
@@ -733,7 +735,11 @@
auto error = mDisplayData[displayId].hwcDisplay->setActiveConfigWithConstraints(hwcModeId,
constraints,
outTimeline);
- RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ if (error == hal::Error::CONFIG_FAILED) {
+ RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId,
+ FAILED_TRANSACTION);
+ }
+ RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e21ce1d..52662cf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -55,6 +55,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <aidl/android/hardware/graphics/composer3/OutputType.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -112,12 +113,14 @@
float dpiY = -1.f;
int32_t configGroup = -1;
std::optional<hal::VrrConfig> vrrConfig;
+ OutputType hdrOutputType;
friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
<< " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
<< mode.dpiY << " group=" << mode.configGroup
- << " vrrConfig=" << to_string(mode.vrrConfig).c_str();
+ << " vrrConfig=" << to_string(mode.vrrConfig).c_str()
+ << " hdrOutputType=" << toString(mode.hdrOutputType);
}
};
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index e3d9622..568d758 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -17,16 +17,21 @@
#pragma once
#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <android/hardware/graphics/composer/2.4/types.h>
#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
#include <aidl/android/hardware/graphics/composer3/VrrConfig.h>
+#include <ftl/enum.h>
+
#define ERROR_HAS_CHANGES 5
namespace android {
@@ -46,7 +51,6 @@
using types::V1_2::Dataspace;
using types::V1_2::PixelFormat;
-using V2_1::Error;
using V2_4::IComposer;
using V2_4::IComposerCallback;
using V2_4::IComposerClient;
@@ -78,6 +82,22 @@
using DisplayConfiguration = V3_0::DisplayConfiguration;
using VrrConfig = V3_0::VrrConfig;
+enum class Error : int32_t {
+ NONE = static_cast<int32_t>(V2_1::Error::NONE),
+ BAD_CONFIG = static_cast<int32_t>(V2_1::Error::BAD_CONFIG),
+ BAD_DISPLAY = static_cast<int32_t>(V2_1::Error::BAD_DISPLAY),
+ BAD_LAYER = static_cast<int32_t>(V2_1::Error::BAD_LAYER),
+ BAD_PARAMETER = static_cast<int32_t>(V2_1::Error::BAD_PARAMETER),
+ NO_RESOURCES = static_cast<int32_t>(V2_1::Error::NO_RESOURCES),
+ NOT_VALIDATED = static_cast<int32_t>(V2_1::Error::NOT_VALIDATED),
+ UNSUPPORTED = static_cast<int32_t>(V2_1::Error::UNSUPPORTED),
+ SEAMLESS_NOT_ALLOWED = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_ALLOWED),
+ SEAMLESS_NOT_POSSIBLE = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_POSSIBLE),
+ CONFIG_FAILED = V3_0::IComposerClient::EX_CONFIG_FAILED,
+ PICTURE_PROFILE_MAX_EXCEEDED = V3_0::IComposerClient::EX_PICTURE_PROFILE_MAX_EXCEEDED,
+ ftl_last = PICTURE_PROFILE_MAX_EXCEEDED
+};
+
} // namespace hardware::graphics::composer::hal
inline bool hasChangesError(hardware::graphics::composer::hal::Error error) {
@@ -210,7 +230,11 @@
}
inline std::string to_string(hardware::graphics::composer::hal::Error error) {
- return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
+ // 5 is reserved for historical reason, during validation 5 means has changes.
+ if (hasChangesError(error)) {
+ return "HAS_CHANGES";
+ }
+ return ftl::enum_string(error);
}
// For utils::Dumper ADL.
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index e359a26..5703a2d 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -27,6 +27,7 @@
#include <SurfaceFlingerProperties.h>
#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <android/binder_manager.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
#include <common/trace.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
@@ -173,7 +174,7 @@
};
// assume NO_RESOURCES when Status::isOk returns false
-constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_1::Error kDefaultError = V2_1::Error::NO_RESOURCES;
constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
template <typename T, typename U>
@@ -181,7 +182,7 @@
return (ret.isOk()) ? static_cast<T>(ret) : static_cast<T>(default_val);
}
-Error unwrapRet(Return<Error>& ret) {
+V2_1::Error unwrapRet(Return<V2_1::Error>& ret) {
return unwrapRet(ret, kDefaultError);
}
@@ -235,7 +236,7 @@
});
} else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError == Error::NONE) {
+ if (tmpError == V2_1::Error::NONE) {
mClient = tmpClient;
mClient_2_2 = tmpClient;
mClient_2_3 = tmpClient;
@@ -243,7 +244,7 @@
});
} else {
mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError != Error::NONE) {
+ if (tmpError != V2_1::Error::NONE) {
return;
}
@@ -325,14 +326,14 @@
Error HidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
Display* outDisplay) {
const uint32_t bufferSlotCount = 1;
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_2) {
mClient_2_2->createVirtualDisplay_2_2(width, height,
static_cast<types::V1_1::PixelFormat>(*format),
bufferSlotCount,
[&](const auto& tmpError, const auto& tmpDisplay,
const auto& tmpFormat) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -346,7 +347,7 @@
bufferSlotCount,
[&](const auto& tmpError, const auto& tmpDisplay,
const auto& tmpFormat) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -361,7 +362,7 @@
Error HidlComposer::destroyVirtualDisplay(Display display) {
auto ret = mClient->destroyVirtualDisplay(display);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::acceptDisplayChanges(Display display) {
@@ -371,10 +372,10 @@
}
Error HidlComposer::createLayer(Display display, Layer* outLayer) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->createLayer(display, kMaxLayerBufferCount,
[&](const auto& tmpError, const auto& tmpLayer) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -387,13 +388,13 @@
Error HidlComposer::destroyLayer(Display display, Layer layer) {
auto ret = mClient->destroyLayer(display, layer);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::getActiveConfig(Display display, Config* outConfig) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -412,11 +413,11 @@
}
Error HidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_3) {
mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -425,7 +426,7 @@
});
} else if (mClient_2_2) {
mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -436,7 +437,7 @@
});
} else {
mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -451,7 +452,7 @@
Error HidlComposer::getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_4) {
mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
[&](const auto& tmpError, const auto& tmpValue) {
@@ -466,7 +467,7 @@
mClient->getDisplayAttribute(display, config,
static_cast<V2_1::IComposerClient::Attribute>(attribute),
[&](const auto& tmpError, const auto& tmpValue) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -479,9 +480,9 @@
}
Error HidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -499,9 +500,9 @@
}
Error HidlComposer::getDisplayName(Display display, std::string* outName) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -520,9 +521,9 @@
}
Error HidlComposer::getDozeSupport(Display display, bool* outSupport) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -541,14 +542,14 @@
Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_3) {
mClient_2_3->getHdrCapabilities_2_3(display,
[&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -564,7 +565,7 @@
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -606,7 +607,7 @@
Error HidlComposer::setActiveConfig(Display display, Config config) {
auto ret = mClient->setActiveConfig(display, config);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
@@ -625,7 +626,7 @@
}
Error HidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
- hardware::Return<Error> ret(kDefaultError);
+ hardware::Return<V2_1::Error> ret(kDefaultError);
if (mClient_2_3) {
ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
} else if (mClient_2_2) {
@@ -634,7 +635,7 @@
} else {
ret = mClient->setColorMode(display, static_cast<types::V1_0::ColorMode>(mode));
}
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setColorTransform(Display display, const float* matrix) {
@@ -654,25 +655,25 @@
}
Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
- Return<Error> ret(Error::UNSUPPORTED);
+ Return<V2_1::Error> ret(V2_1::Error::UNSUPPORTED);
if (mClient_2_2) {
ret = mClient_2_2->setPowerMode_2_2(display, mode);
} else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
}
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
auto ret = mClient->setVsyncEnabled(display, enabled);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setClientTargetSlotCount(Display display) {
const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
@@ -903,7 +904,7 @@
// set up new input command queue if necessary
if (queueChanged) {
auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
- auto error = unwrapRet(ret);
+ auto error = static_cast<Error>(unwrapRet(ret));
if (error != Error::NONE) {
mWriter.reset();
return error;
@@ -915,17 +916,17 @@
return Error::NONE;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
hardware::Return<void> ret;
auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
const auto& tmpOutLength, const auto& tmpOutHandles) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
// set up new output command queue if necessary
if (error == Error::NONE && tmpOutChanged) {
- error = kDefaultError;
+ error = static_cast<Error>(kDefaultError);
mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1000,11 +1001,11 @@
return keys;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_3) {
mClient_2_3->getPerFrameMetadataKeys_2_3(display,
[&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
ALOGW("getPerFrameMetadataKeys failed "
"with %d",
@@ -1016,7 +1017,7 @@
} else {
mClient_2_2
->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
return;
@@ -1039,10 +1040,10 @@
return Error::NONE;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1066,10 +1067,10 @@
return Error::NONE;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
[&](const auto& tmpError, const auto& tmpMatrix) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1087,11 +1088,11 @@
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_3->getDisplayIdentificationData(display,
[&](const auto& tmpError, const auto& tmpPort,
const auto& tmpData) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1123,13 +1124,13 @@
if (!mClient_2_3) {
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_3->getDisplayedContentSamplingAttributes(display,
[&](const auto tmpError,
const auto& tmpFormat,
const auto& tmpDataspace,
const auto& tmpComponentMask) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error == Error::NONE) {
*outFormat = tmpFormat;
*outDataspace = tmpDataspace;
@@ -1149,8 +1150,9 @@
auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
: V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
- return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
- maxFrames);
+ auto ret = mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+ maxFrames);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
@@ -1161,12 +1163,12 @@
if (!mClient_2_3) {
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
[&](const auto tmpError, auto tmpNumFrames,
const auto& tmpSamples0, const auto& tmpSamples1,
const auto& tmpSamples2, const auto& tmpSamples3) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error == Error::NONE) {
outStats->numFrames = tmpNumFrames;
outStats->component_0_sample = tmpSamples0;
@@ -1196,7 +1198,8 @@
if (!mClient_2_3) {
return Error::UNSUPPORTED;
}
- return mClient_2_3->setDisplayBrightness(display, brightness);
+ auto ret = mClient_2_3->setDisplayBrightness(display, brightness);
+ return static_cast<Error>(unwrapRet(ret));
}
// Composer HAL 2.4
@@ -1273,19 +1276,18 @@
return error;
}
-V2_4::Error HidlComposer::setActiveConfigWithConstraints(
+Error HidlComposer::setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) {
- using Error = V2_4::Error;
if (!mClient_2_4) {
return Error::UNSUPPORTED;
}
- Error error = kDefaultError_2_4;
+ Error error = static_cast<Error>(kDefaultError_2_4);
mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
[&](const auto& tmpError, const auto& tmpTimeline) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 9a89dba..42ba9a9 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -60,7 +60,6 @@
using V2_1::Config;
using V2_1::Display;
-using V2_1::Error;
using V2_1::Layer;
using V2_4::CommandReaderBase;
using V2_4::CommandWriterBase;
@@ -308,7 +307,7 @@
V2_4::Error getDisplayConnectionType(Display display,
IComposerClient::DisplayConnectionType* outType) override;
V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
- V2_4::Error setActiveConfigWithConstraints(
+ Error setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
diff --git a/services/surfaceflinger/PowerAdvisor/Android.bp b/services/surfaceflinger/PowerAdvisor/Android.bp
index 7352f7a..4efbcb9 100644
--- a/services/surfaceflinger/PowerAdvisor/Android.bp
+++ b/services/surfaceflinger/PowerAdvisor/Android.bp
@@ -19,9 +19,6 @@
aidl_interface {
name: "android.adpf.sessionmanager_aidl",
- defaults: [
- "android.hardware.power-aidl",
- ],
srcs: [
"aidl/android/adpf/*.aidl",
],
@@ -39,10 +36,6 @@
enabled: true,
},
},
- imports: [
- "android.hardware.common.fmq-V1",
- "android.hardware.common-V2",
- ],
}
cc_defaults {
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/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 668fa54..97f1f8f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1532,6 +1532,9 @@
mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
+ mAllFrameRates = filterRefreshRates(FpsRanges(getSupportedFrameRateRangeLocked(),
+ getSupportedFrameRateRangeLocked()),
+ "full frame rates");
}
bool RefreshRateSelector::isVrrDevice() const {
@@ -1560,16 +1563,24 @@
std::vector<float> RefreshRateSelector::getSupportedFrameRates() const {
std::scoped_lock lock(mLock);
// TODO(b/356986687) Remove the limit once we have the anchor list implementation.
- const size_t frameRatesSize = std::min<size_t>(11, mPrimaryFrameRates.size());
+ const size_t frameRatesSize = std::min<size_t>(11, mAllFrameRates.size());
std::vector<float> supportedFrameRates;
supportedFrameRates.reserve(frameRatesSize);
- std::transform(mPrimaryFrameRates.rbegin(),
- mPrimaryFrameRates.rbegin() + static_cast<int>(frameRatesSize),
+ std::transform(mAllFrameRates.rbegin(),
+ mAllFrameRates.rbegin() + static_cast<int>(frameRatesSize),
std::back_inserter(supportedFrameRates),
[](FrameRateMode mode) { return mode.fps.getValue(); });
return supportedFrameRates;
}
+FpsRange RefreshRateSelector::getSupportedFrameRateRangeLocked() const {
+ using fps_approx_ops::operator<;
+ if (mMaxRefreshRateModeIt->second->getPeakFps() < kMinSupportedFrameRate) {
+ return {mMaxRefreshRateModeIt->second->getPeakFps(), kMinSupportedFrameRate};
+ }
+ return {kMinSupportedFrameRate, mMaxRefreshRateModeIt->second->getPeakFps()};
+}
+
auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
std::lock_guard lock(mLock);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 508f9d7..8e173b1 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -553,6 +553,7 @@
// Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mAllFrameRates GUARDED_BY(mLock);
// Caches whether the device is VRR-compatible based on the active display mode.
std::atomic_bool mIsVrrDevice = false;
@@ -597,6 +598,9 @@
// Used to detect (lack of) frame activity.
ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
std::atomic<bool> mIdleTimerStarted = false;
+
+ // Returns the range of supported frame rates.
+ FpsRange getSupportedFrameRateRangeLocked() const REQUIRES(mLock);
};
} // namespace android::scheduler
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 f9e59b9..59b1917 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -172,6 +172,7 @@
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OutputType.h>
#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
#undef NO_THREAD_SAFETY_ANALYSIS
@@ -649,11 +650,12 @@
}
}
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
- ui::PixelFormat format) {
+VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
+ const std::string& uniqueId) {
if (auto& generator = mVirtualDisplayIdGenerators.hal) {
if (const auto id = generator->generateId()) {
if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
+ acquireVirtualDisplaySnapshot(*id, uniqueId);
return *id;
}
@@ -667,6 +669,7 @@
const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
+ acquireVirtualDisplaySnapshot(*id, uniqueId);
return *id;
}
@@ -674,6 +677,7 @@
if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
if (auto& generator = mVirtualDisplayIdGenerators.hal) {
generator->releaseId(*id);
+ releaseVirtualDisplaySnapshot(*id);
}
return;
}
@@ -681,6 +685,14 @@
const auto id = GpuVirtualDisplayId::tryCast(displayId);
LOG_ALWAYS_FATAL_IF(!id);
mVirtualDisplayIdGenerators.gpu.releaseId(*id);
+ releaseVirtualDisplaySnapshot(*id);
+}
+
+void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) {
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ if (!mVirtualDisplays.erase(displayId)) {
+ ALOGW("%s: Virtual display snapshot was not removed", __func__);
+ }
}
std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
@@ -1532,8 +1544,15 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
- constraints, outTimeline)) {
+ 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;
}
@@ -3497,6 +3516,9 @@
DisplayModes newModes;
for (const auto& hwcMode : hwcModes) {
const auto id = nextModeId++;
+ OutputType hdrOutputType = FlagManager::getInstance().connected_display_hdr()
+ ? hwcMode.hdrOutputType
+ : OutputType::INVALID;
newModes.try_emplace(id,
DisplayMode::Builder(hwcMode.hwcId)
.setId(id)
@@ -3507,6 +3529,7 @@
.setDpiX(hwcMode.dpiX)
.setDpiY(hwcMode.dpiY)
.setGroup(hwcMode.configGroup)
+ .setHdrOutputType(hdrOutputType)
.build());
}
@@ -3797,7 +3820,7 @@
if (const auto& physical = state.physical) {
builder.setId(physical->id);
} else {
- builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
+ builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId));
}
builder.setPixels(resolution);
@@ -4387,7 +4410,7 @@
scheduleNotifyExpectedPresentHint(displayId);
}
-void SurfaceFlinger::onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) {
+void SurfaceFlinger::onCommitNotComposited() {
if (FlagManager::getInstance().commit_not_composited()) {
mFrameTimeline->onCommitNotComposited();
}
@@ -5784,6 +5807,14 @@
utils::Dumper::Section section(dumper,
ftl::Concat("Virtual Display ", displayId.value).str());
display->dump(dumper);
+
+ if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) {
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value());
+ if (virtualSnapshotIt != mVirtualDisplays.end()) {
+ virtualSnapshotIt->second.dump(dumper);
+ }
+ }
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 211f374..1e2c087 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -73,6 +73,7 @@
#include "BackgroundExecutor.h"
#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
+#include "Display/VirtualDisplaySnapshot.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayIdGenerator.h"
@@ -700,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;
@@ -1075,8 +1076,20 @@
void enableHalVirtualDisplays(bool);
// Virtual display lifecycle for ID generation and HAL allocation.
- VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
+ VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId)
+ REQUIRES(mStateLock);
+ template <typename ID>
+ void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) {
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ const bool emplace_success =
+ mVirtualDisplays.try_emplace(displayId, displayId, uniqueId).second;
+ if (!emplace_success) {
+ ALOGW("%s: Virtual display snapshot with the same ID already exists", __func__);
+ }
+ }
+
void releaseVirtualDisplay(VirtualDisplayId);
+ void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId);
// Returns a display other than `mActiveDisplayId` that can be activated, if any.
sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext);
@@ -1277,6 +1290,10 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
+ mutable std::mutex mVirtualDisplaysMutex;
+ ftl::SmallMap<VirtualDisplayId, const display::VirtualDisplaySnapshot, 2> mVirtualDisplays
+ GUARDED_BY(mVirtualDisplaysMutex);
+
// The inner or outer display for foldables, while unfolded or folded, respectively.
std::atomic<PhysicalDisplayId> mActiveDisplayId;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index f257c7c..e28a0c1 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -119,6 +119,7 @@
/// Trunk stable readonly flags ///
DUMP_ACONFIG_FLAG(adpf_fmq_sf);
+ DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum);
DUMP_ACONFIG_FLAG(connected_display);
DUMP_ACONFIG_FLAG(enable_small_area_detection);
DUMP_ACONFIG_FLAG(stable_edid_ids);
@@ -218,6 +219,7 @@
/// Trunk stable readonly flags ///
FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "")
+FLAG_MANAGER_ACONFIG_FLAG(arr_setframerate_gte_enum, "debug.sf.arr_setframerate_gte_enum")
FLAG_MANAGER_ACONFIG_FLAG(connected_display, "")
FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "")
FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids")
@@ -257,7 +259,7 @@
FLAG_MANAGER_ACONFIG_FLAG(force_compile_graphite_renderengine, "");
FLAG_MANAGER_ACONFIG_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
FLAG_MANAGER_ACONFIG_FLAG(display_config_error_hal, "");
-FLAG_MANAGER_ACONFIG_FLAG(connected_display_hdr, "");
+FLAG_MANAGER_ACONFIG_FLAG(connected_display_hdr, "debug.sf.connected_display_hdr");
FLAG_MANAGER_ACONFIG_FLAG(deprecate_frame_tracker, "");
FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index a461627..1a857c8 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -56,6 +56,7 @@
bool graphite_renderengine_preview_rollout() const;
/// Trunk stable readonly flags ///
+ bool arr_setframerate_gte_enum() const;
bool adpf_fmq_sf() const;
bool connected_display() const;
bool frame_rate_category_mrr() const;
diff --git a/services/surfaceflinger/surfaceflinger.logtags b/services/surfaceflinger/surfaceflinger.logtags
index e68d9f5..6dbe0dd 100644
--- a/services/surfaceflinger/surfaceflinger.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
# 60100 - 60199 reserved for surfaceflinger
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index dfce722..fc0fabd 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -35,6 +35,14 @@
} # arr_setframerate_api
flag {
+ name: "arr_setframerate_gte_enum"
+ namespace: "core_graphics"
+ description: "Exposes GTE (greater than or equal to) enum for Android 16"
+ bug: "380949716"
+ is_fixed_read_only: true
+} # arr_setframerate_gte_enum
+
+flag {
name: "arr_surfacecontrol_setframerate_api"
namespace: "core_graphics"
description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16"
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index d971150..29a1fab 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -20,6 +20,7 @@
#include "Display/DisplayModeController.h"
#include "Display/DisplaySnapshot.h"
#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
#include "DisplayIdentificationTestHelpers.h"
#include "FpsOps.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -103,7 +104,7 @@
EXPECT_CALL(*mComposerHal,
setActiveConfigWithConstraints(kHwcDisplayId, hwcModeId, constraints, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::V2_4::Error::NONE)));
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)));
return constraints;
}
@@ -183,7 +184,8 @@
hal::VsyncPeriodChangeTimeline timeline;
const auto constraints = expectModeSet(modeRequest, timeline);
- EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+ mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
mDmc.clearDesiredMode(mDisplayId);
@@ -210,7 +212,8 @@
hal::VsyncPeriodChangeTimeline timeline;
auto constraints = expectModeSet(modeRequest, timeline);
- EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+ mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
// No action since a mode switch has already been initiated.
@@ -223,7 +226,8 @@
constexpr bool kSubsequent = true;
constraints = expectModeSet(modeRequest, timeline, kSubsequent);
- EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+ mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getPendingMode(mDisplayId));
mDmc.clearDesiredMode(mDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index e0753a3..ba2d3e2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -69,7 +69,7 @@
using ::testing::StrictMock;
struct HWComposerTest : testing::Test {
- using HalError = hardware::graphics::composer::V2_1::Error;
+ using HalError = hal::Error;
Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>();
impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)};
@@ -384,6 +384,7 @@
const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
const float expectedDpiX = (kWidth * kMmPerInch / size.width);
const float expectedDpiY = (kHeight * kMmPerInch / size.height);
+ const OutputType hdrOutputType = OutputType::SYSTEM;
const hal::VrrConfig vrrConfig =
hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
.notifyExpectedPresentConfig = hal::VrrConfig::
@@ -394,7 +395,8 @@
.height = kHeight,
.configGroup = kConfigGroup,
.vsyncPeriod = kVsyncPeriod,
- .vrrConfig = vrrConfig};
+ .vrrConfig = vrrConfig,
+ .hdrOutputType = hdrOutputType};
EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
.WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
@@ -410,6 +412,7 @@
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
+ EXPECT_EQ(modes.front().hdrOutputType, hdrOutputType);
if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
EXPECT_EQ(modes.front().dpiX, -1);
EXPECT_EQ(modes.front().dpiY, -1);
@@ -435,6 +438,7 @@
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
+ EXPECT_EQ(modes.front().hdrOutputType, hdrOutputType);
EXPECT_EQ(modes.front().dpiX, kDpi);
EXPECT_EQ(modes.front().dpiY, kDpi);
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/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 80b2b8d..9b3cba5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -4667,5 +4667,47 @@
EXPECT_EQ(120_Hz, primaryRefreshRates[i].modePtr->getPeakFps());
}
}
+
+TEST_P(RefreshRateSelectorTest, getSupportedFrameRates) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_60_90, kModeId90);
+ const FpsRange range60 = {0_Hz, 60_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {range60, range60}, {range60, range60}}));
+
+ // Irrespective of the policy we get the full range of possible frame rates
+ const std::vector<float> expected = {90.0f, 60.0f, 45.0f, 30.0f, 22.5f, 20.0f};
+
+ const auto allSupportedFrameRates = selector.getSupportedFrameRates();
+ ASSERT_EQ(expected.size(), allSupportedFrameRates.size());
+ for (size_t i = 0; i < expected.size(); i++) {
+ EXPECT_EQ(expected[i], allSupportedFrameRates[i])
+ << "expected " << expected[i] << " received " << allSupportedFrameRates[i];
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getSupportedFrameRatesArr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ const auto selector = createSelector(kVrrMode_120, kModeId120);
+
+ const std::vector<float> expected = {120.0f, 80.0f, 60.0f, 48.0f, 40.0f, 34.285f,
+ 30.0f, 26.666f, 24.0f, 21.818f, 20.0f};
+
+ const auto allSupportedFrameRates = selector.getSupportedFrameRates();
+ ASSERT_EQ(expected.size(), allSupportedFrameRates.size());
+ constexpr float kEpsilon = 0.001f;
+ for (size_t i = 0; i < expected.size(); i++) {
+ EXPECT_TRUE(std::abs(expected[i] - allSupportedFrameRates[i]) <= kEpsilon)
+ << "expected " << expected[i] << " received " << allSupportedFrameRates[i];
+ }
+}
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 8699621..b0dd5c2 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -34,7 +34,7 @@
static_cast<hal::HWConfigId>( \
ftl::to_underlying(modeId)), \
_, _)) \
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)))
namespace android {
namespace {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 20a3315..6cc6322 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -18,12 +18,13 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <gui/SurfaceComposerClient.h>
+#include "DisplayHardware/Hal.h"
#include "DisplayTransactionTestHelpers.h"
namespace android {
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::hal::Error;
class NotifyExpectedPresentTest : public DisplayTransactionTest {
public:
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index c3934e6..3ae5ed9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -21,6 +21,8 @@
#include "CommitAndCompositeTest.h"
+#include "DisplayHardware/Hal.h"
+
using namespace std::chrono_literals;
using testing::_;
using testing::Return;
@@ -40,7 +42,7 @@
EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
- return hardware::graphics::composer::V2_1::Error::NONE;
+ return hardware::graphics::composer::hal::Error::NONE;
});
EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(1);
@@ -61,7 +63,7 @@
EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
- return hardware::graphics::composer::V2_1::Error::NONE;
+ return hardware::graphics::composer::hal::Error::NONE;
});
EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(0);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 88052db..0d5266e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -20,6 +20,7 @@
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/Hal.h"
namespace android {
@@ -34,9 +35,9 @@
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::composer::hal::Error;
using android::hardware::graphics::composer::V2_1::Config;
using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
using android::hardware::graphics::composer::V2_1::IComposer;
using android::hardware::graphics::composer::V2_1::Layer;
using android::hardware::graphics::composer::V2_4::IComposerCallback;
@@ -142,8 +143,8 @@
V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
MOCK_METHOD4(setActiveConfigWithConstraints,
- V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
- VsyncPeriodChangeTimeline*));
+ Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+ VsyncPeriodChangeTimeline*));
MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index fa74492..ec065a7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -17,6 +17,7 @@
#pragma once
#include <gmock/gmock.h>
+#include <cstdint>
#include "DisplayHardware/HWC2.h"
@@ -85,8 +86,8 @@
MOCK_METHOD(ftl::Future<hal::Error>, setDisplayBrightness,
(float, float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
- (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
- hal::VsyncPeriodChangeTimeline *),
+ (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline*),
(override));
MOCK_METHOD(hal::Error, setBootDisplayConfig, (hal::HWConfigId), (override));
MOCK_METHOD(hal::Error, clearBootDisplayConfig, (), (override));
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 {}
};