Merge "Delete TestHelpers.h" into main
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 5719a09..cd4926a 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -313,12 +313,6 @@
static const char* k_traceBufferSizePath =
"buffer_size_kb";
-#if 0
-// TODO: Re-enable after stabilization
-static const char* k_traceCmdlineSizePath =
- "saved_cmdlines_size";
-#endif
-
static const char* k_tracingOverwriteEnablePath =
"options/overwrite";
@@ -545,18 +539,6 @@
return writeStr(k_traceBufferSizePath, str);
}
-#if 0
-// TODO: Re-enable after stabilization
-// Set the default size of cmdline hashtable
-static bool setCmdlineSize()
-{
- if (fileExists(k_traceCmdlineSizePath)) {
- return writeStr(k_traceCmdlineSizePath, "8192");
- }
- return true;
-}
-#endif
-
// Set the clock to the best available option while tracing. Use 'boot' if it's
// available; otherwise, use 'mono'. If neither are available use 'global'.
// Any write to the trace_clock sysfs file will reset the buffer, so only
@@ -870,8 +852,6 @@
ok &= setCategoriesEnableFromFile(g_categoriesFile);
ok &= setTraceOverwriteEnable(g_traceOverwrite);
ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
- // TODO: Re-enable after stabilization
- //ok &= setCmdlineSize();
ok &= setClock();
ok &= setPrintTgidEnableIfPresent(true);
ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index a3e29a8..fdac5db 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -13,6 +13,8 @@
# Access control to these files is now entirely in selinux policy.
chmod 0666 /sys/kernel/debug/tracing/trace_clock
chmod 0666 /sys/kernel/tracing/trace_clock
+ chmod 0666 /sys/kernel/debug/tracing/buffer_percent
+ chmod 0666 /sys/kernel/tracing/buffer_percent
chmod 0666 /sys/kernel/debug/tracing/buffer_size_kb
chmod 0666 /sys/kernel/tracing/buffer_size_kb
chmod 0666 /sys/kernel/debug/tracing/options/overwrite
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5f109fb..826a8db 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3280,6 +3280,12 @@
// duration is logged into MYLOG instead.
PrintHeader();
+ bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
+ if (options_->use_predumped_ui_data && !system_trace_exists) {
+ MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available");
+ options_->use_predumped_ui_data = false;
+ }
+
std::future<std::string> snapshot_system_trace;
bool is_dumpstate_restricted =
@@ -3468,7 +3474,7 @@
"tracing", "save-for-bugreport"});
}
- if (!android_tracing_perfetto_protolog()) {
+ if (!android_tracing_perfetto_protolog_tracing()) {
dumpTracesForBugReportCommands.push_back({"dumpsys", "activity", "service",
"SystemUIService", "WMShell", "protolog",
"save-for-bugreport"});
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index 28bd793..ae7d8e0 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -50,6 +50,12 @@
exit 1
fi
+if pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
+ # Handled by Pre-reboot Dexopt.
+ exit 0
+fi
+echo "Pre-reboot Dexopt not enabled. Fall back to otapreopt."
+
if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then
# We require an updated chroot wrapper that reads dexopt commands from stdin.
# Even if we kept compat with the old binary, the OTA preopt wouldn't work due
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 8b2842a..eedf141 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -353,6 +353,12 @@
}
prebuilt_etc {
+ name: "android.software.contextualsearch.prebuilt.xml",
+ src: "android.software.contextualsearch.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.device_id_attestation.prebuilt.xml",
src: "android.software.device_id_attestation.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/android/input.h b/include/android/input.h
index b5c1e5c..fec56f0 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1492,7 +1492,10 @@
/**
* Creates a java android.view.InputEvent object that is a copy of the specified native
- * {@link AInputEvent}. Returns null on error
+ * {@link AInputEvent}.
+ *
+ * Specified {@link AInputEvent} is require to be a valid {@link MotionEvent} or {@link KeyEvent}
+ * object.
*
* Available since API level 35.
*/
diff --git a/include/input/Input.h b/include/input/Input.h
index a84dcfc..ddc3768 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -662,10 +662,6 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXOffset() const { return mTransform.tx(); }
-
- inline float getYOffset() const { return mTransform.ty(); }
-
inline const ui::Transform& getTransform() const { return mTransform; }
std::optional<ui::Rotation> getSurfaceRotation() const;
@@ -870,12 +866,32 @@
void copyFrom(const MotionEvent* other, bool keepHistory);
+ // Initialize this event by keeping only the pointers from "other" that are in splitPointerIds.
+ void splitFrom(const MotionEvent& other, std::bitset<MAX_POINTER_ID + 1> splitPointerIds,
+ int32_t newEventId);
+
void addSample(
nsecs_t eventTime,
const PointerCoords* pointerCoords);
void offsetLocation(float xOffset, float yOffset);
+ /**
+ * Get the X offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawXOffset() const;
+
+ /**
+ * Get the Y offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawYOffset() const;
+
void scale(float globalScaleFactor);
// Set 3x3 perspective matrix transformation.
@@ -910,6 +926,11 @@
static std::string actionToString(int32_t action);
+ static std::tuple<int32_t /*action*/, std::vector<PointerProperties>,
+ std::vector<PointerCoords>>
+ split(int32_t action, int32_t flags, int32_t historySize, const std::vector<PointerProperties>&,
+ const std::vector<PointerCoords>&, std::bitset<MAX_POINTER_ID + 1> splitPointerIds);
+
// MotionEvent will transform various axes in different ways, based on the source. For
// example, the x and y axes will not have any offsets/translations applied if it comes from a
// relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 2d23b97..c0c5e24 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -118,6 +118,16 @@
return *this;
}
+ MotionEventBuilder& transform(ui::Transform t) {
+ mTransform = t;
+ return *this;
+ }
+
+ MotionEventBuilder& rawTransform(ui::Transform t) {
+ mRawTransform = t;
+ return *this;
+ }
+
MotionEvent build() {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
@@ -134,12 +144,11 @@
}
MotionEvent event;
- static const ui::Transform kIdentityTransform;
event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
- MotionClassification::NONE, kIdentityTransform,
+ MotionClassification::NONE, mTransform,
/*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
+ mRawYCursorPosition, mRawTransform, mDownTime, mEventTime,
mPointers.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -156,6 +165,8 @@
int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ ui::Transform mTransform;
+ ui::Transform mRawTransform;
std::vector<PointerBuilder> mPointers;
};
diff --git a/include/powermanager/HalResult.h b/include/powermanager/HalResult.h
new file mode 100644
index 0000000..7fe3822
--- /dev/null
+++ b/include/powermanager/HalResult.h
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_status.h>
+#include <android/hardware/power/1.0/IPower.h>
+#include <binder/Status.h>
+#include <hidl/HidlSupport.h>
+#include <string>
+
+namespace android::power {
+
+static bool checkUnsupported(const ndk::ScopedAStatus& ndkStatus) {
+ return ndkStatus.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||
+ ndkStatus.getStatus() == STATUS_UNKNOWN_TRANSACTION;
+}
+
+static bool checkUnsupported(const binder::Status& status) {
+ return status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == UNKNOWN_TRANSACTION;
+}
+
+// Result of a call to the Power HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
+ static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
+ static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
+ if (checkUnsupported(status)) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(std::forward<T>(data));
+ }
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<T> fromStatus(const binder::Status& status, T& data) {
+ return HalResult<T>::fromStatus(status, T{data});
+ }
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T&& data) {
+ if (checkUnsupported(ndkStatus)) {
+ return HalResult<T>::unsupported();
+ }
+ if (ndkStatus.isOk()) {
+ return HalResult<T>::ok(std::forward<T>(data));
+ }
+ return HalResult<T>::failed(std::string(ndkStatus.getDescription()));
+ }
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T& data) {
+ return HalResult<T>::fromStatus(ndkStatus, T{data});
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
+ return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
+ : HalResult<T>::failed(ret.description());
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
+ return HalResult<T>::fromReturn(ret, T{data});
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T&& data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
+ : HalResult<T>::failed(ret.description());
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T& data) {
+ return HalResult<T>::fromReturn(ret, status, T{data});
+ }
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T&& value)
+ : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(const binder::Status& status) {
+ if (checkUnsupported(status)) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<void> fromStatus(const ndk::ScopedAStatus& ndkStatus) {
+ if (ndkStatus.isOk()) {
+ return HalResult<void>::ok();
+ }
+ if (checkUnsupported(ndkStatus)) {
+ return HalResult<void>::unsupported();
+ }
+ return HalResult<void>::failed(ndkStatus.getDescription());
+ }
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+ }
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+} // namespace android::power
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index c50bc4a..7e0bd5b 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
#include <powermanager/PowerHalWrapper.h>
+#include <powermanager/PowerHintSessionWrapper.h>
namespace android {
@@ -38,6 +39,7 @@
virtual std::unique_ptr<HalWrapper> connect();
virtual void reset();
+ virtual int32_t getAidlVersion();
};
// -------------------------------------------------------------------------------------------------
@@ -59,14 +61,13 @@
int32_t durationMs) override;
virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
bool enabled) override;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) override;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(
int tgid, int uid) override;
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index cbbfa59..ab66336 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -36,6 +36,8 @@
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
static sp<hardware::power::V1_3::IPower> loadHidlV1_3();
+ // Returns aidl interface version, or 0 if AIDL is not used
+ static int32_t getAidlVersion();
private:
static std::mutex gHalMutex;
@@ -48,6 +50,8 @@
static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+ static int32_t gAidlInterfaceVersion;
+
PowerHalLoader() = delete;
~PowerHalLoader() = delete;
};
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index e2da014..6e347a9 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -26,6 +26,9 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
+#include <powermanager/HalResult.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+
#include <binder/Status.h>
#include <utility>
@@ -41,134 +44,6 @@
OFF = 2,
};
-// Result of a call to the Power HAL wrapper, holding data if successful.
-template <typename T>
-class HalResult {
-public:
- static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
- static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
- static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
- static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
-
- static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<T>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<T>::ok(std::forward<T>(data));
- }
- return HalResult<T>::failed(std::string(status.toString8().c_str()));
- }
-
- static HalResult<T> fromStatus(const binder::Status& status, T& data) {
- return HalResult<T>::fromStatus(status, T{data});
- }
-
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T&& data) {
- if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<T>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<T>::ok(std::forward<T>(data));
- }
- return HalResult<T>::failed(std::string(status.getDescription()));
- }
-
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T& data) {
- return HalResult<T>::fromStatus(status, T{data});
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
- return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
- : HalResult<T>::failed(ret.description());
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
- return HalResult<T>::fromReturn(ret, T{data});
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T&& data) {
- return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
- : HalResult<T>::failed(ret.description());
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T& data) {
- return HalResult<T>::fromReturn(ret, status, T{data});
- }
-
- // This will throw std::bad_optional_access if this result is not ok.
- const T& value() const { return mValue.value(); }
- bool isOk() const { return !mUnsupported && mValue.has_value(); }
- bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
- bool isUnsupported() const { return mUnsupported; }
- const char* errorMessage() const { return mErrorMessage.c_str(); }
-
-private:
- std::optional<T> mValue;
- std::string mErrorMessage;
- bool mUnsupported;
-
- explicit HalResult(T&& value)
- : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
- explicit HalResult(std::string errorMessage, bool unsupported)
- : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
-};
-
-// Empty result of a call to the Power HAL wrapper.
-template <>
-class HalResult<void> {
-public:
- static HalResult<void> ok() { return HalResult(); }
- static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
- static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
-
- static HalResult<void> fromStatus(const binder::Status& status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.toString8().c_str()));
- }
-
- static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
- if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.getDescription()));
- }
-
- template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
- }
-
- bool isOk() const { return !mUnsupported && !mFailed; }
- bool isFailed() const { return !mUnsupported && mFailed; }
- bool isUnsupported() const { return mUnsupported; }
- const char* errorMessage() const { return mErrorMessage.c_str(); }
-
-private:
- std::string mErrorMessage;
- bool mFailed;
- bool mUnsupported;
-
- explicit HalResult(bool unsupported = false)
- : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
- explicit HalResult(std::string errorMessage)
- : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
-};
-
// Wrapper for Power HAL handlers.
class HalWrapper {
public:
@@ -177,14 +52,13 @@
virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) = 0;
virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) = 0;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) = 0;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) = 0;
virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) = 0;
@@ -200,14 +74,13 @@
HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) override;
HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
HalResult<int64_t> getHintSessionPreferredRate() override;
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) override;
@@ -285,14 +158,13 @@
HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) override;
HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
HalResult<int64_t> getHintSessionPreferredRate() override;
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
@@ -307,14 +179,12 @@
std::mutex mBoostMutex;
std::mutex mModeMutex;
std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
- // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
- // Need to increase the array size if more boost supported.
- std::array<
- std::atomic<HalSupport>,
- static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
- 1>
+ std::array<HalSupport,
+ static_cast<int32_t>(
+ *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) +
+ 1>
mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
- std::array<std::atomic<HalSupport>,
+ std::array<HalSupport,
static_cast<int32_t>(
*(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
1>
diff --git a/include/powermanager/PowerHintSessionWrapper.h b/include/powermanager/PowerHintSessionWrapper.h
new file mode 100644
index 0000000..ba6fe77
--- /dev/null
+++ b/include/powermanager/PowerHintSessionWrapper.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/ChannelConfig.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/SessionConfig.h>
+#include <android-base/thread_annotations.h>
+#include "HalResult.h"
+
+namespace android::power {
+
+// Wrapper for power hint sessions, which allows for better mocking,
+// support checking, and failure handling than using hint sessions directly
+class PowerHintSessionWrapper {
+public:
+ virtual ~PowerHintSessionWrapper() = default;
+ PowerHintSessionWrapper(
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>&& session);
+ virtual HalResult<void> updateTargetWorkDuration(int64_t in_targetDurationNanos);
+ virtual HalResult<void> reportActualWorkDuration(
+ const std::vector<::aidl::android::hardware::power::WorkDuration>& in_durations);
+ virtual HalResult<void> pause();
+ virtual HalResult<void> resume();
+ virtual HalResult<void> close();
+ virtual HalResult<void> sendHint(::aidl::android::hardware::power::SessionHint in_hint);
+ virtual HalResult<void> setThreads(const std::vector<int32_t>& in_threadIds);
+ virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type,
+ bool in_enabled);
+ virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig();
+
+private:
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession;
+ int32_t mInterfaceVersion;
+};
+
+} // namespace android::power
\ No newline at end of file
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index c1770b3..35cea81 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -256,7 +256,7 @@
if (const auto* rpcFields = maybeRpcFields()) {
if (binder) {
- status_t status = writeInt32(1); // non-null
+ status_t status = writeInt32(RpcFields::TYPE_BINDER); // non-null
if (status != OK) return status;
uint64_t address;
// TODO(b/167966510): need to undo this if the Parcel is not sent
@@ -266,7 +266,7 @@
status = writeUint64(address);
if (status != OK) return status;
} else {
- status_t status = writeInt32(0); // null
+ status_t status = writeInt32(RpcFields::TYPE_BINDER_NULL); // null
if (status != OK) return status;
}
return finishFlattenBinder(binder);
@@ -740,6 +740,12 @@
return kernelFields->mHasFds;
}
+status_t Parcel::hasBinders(bool* result) const {
+ status_t status = hasBindersInRange(0, dataSize(), result);
+ ALOGE_IF(status != NO_ERROR, "Error %d calling hasBindersInRange()", status);
+ return status;
+}
+
std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const {
std::vector<sp<IBinder>> ret;
@@ -799,6 +805,46 @@
return ret;
}
+status_t Parcel::hasBindersInRange(size_t offset, size_t len, bool* result) const {
+ if (len > INT32_MAX || offset > INT32_MAX) {
+ // Don't accept size_t values which may have come from an inadvertent conversion from a
+ // negative int.
+ return BAD_VALUE;
+ }
+ size_t limit;
+ if (__builtin_add_overflow(offset, len, &limit) || limit > mDataSize) {
+ return BAD_VALUE;
+ }
+ *result = false;
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ size_t pos = kernelFields->mObjects[i];
+ if (pos < offset) continue;
+ if (pos + sizeof(flat_binder_object) > offset + len) {
+ if (kernelFields->mObjectsSorted) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + pos);
+ if (flat->hdr.type == BINDER_TYPE_BINDER || flat->hdr.type == BINDER_TYPE_HANDLE) {
+ *result = true;
+ break;
+ }
+ }
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (const auto* rpcFields = maybeRpcFields()) {
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const {
if (len > INT32_MAX || offset > INT32_MAX) {
// Don't accept size_t values which may have come from an inadvertent conversion from a
@@ -2930,14 +2976,14 @@
return continueWrite(desired);
}
+ releaseObjects();
+
uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (!data && desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
}
- releaseObjects();
-
if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
if (mDataCapacity > desired) {
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index d7096d8..5e18b91 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -101,7 +101,9 @@
void restoreAllowFds(bool lastValue);
bool hasFileDescriptors() const;
+ status_t hasBinders(bool* result) const;
status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const;
+ status_t hasBindersInRange(size_t offset, size_t length, bool* result) const;
// returns all binder objects in the Parcel
std::vector<sp<IBinder>> debugReadAllStrongBinders() const;
@@ -647,6 +649,8 @@
void freeDataNoInit();
void initState();
void scanForFds() const;
+ status_t scanForBinders(bool* result) const;
+
status_t validateReadData(size_t len) const;
void updateWorkSourceRequestHeaderPosition() const;
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 34fc43f..32a70e5 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -23,6 +23,7 @@
using android::BBinder;
using android::IBinder;
using android::IPCThreadState;
+using android::NO_ERROR;
using android::OK;
using android::Parcel;
using android::sp;
@@ -164,6 +165,45 @@
ASSERT_EQ(2, p2.readInt32());
}
+TEST(Parcel, HasBinders) {
+ sp<IBinder> b1 = sp<BBinder>::make();
+
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeStrongBinder(b1);
+
+ bool result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+
+ p1.setDataSize(0); // clear data
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(false, result);
+ p1.writeStrongBinder(b1); // reset with binder data
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+
+ Parcel p3;
+ p3.appendFrom(&p1, 0, p1.dataSize());
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+}
+
+TEST(Parcel, HasBindersInRange) {
+ sp<IBinder> b1 = sp<BBinder>::make();
+ Parcel p1;
+ p1.writeStrongBinder(b1);
+ bool result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBindersInRange(0, p1.dataSize(), &result));
+ ASSERT_EQ(true, result);
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+}
+
TEST(Parcel, AppendWithBinder) {
sp<IBinder> b1 = sp<BBinder>::make();
sp<IBinder> b2 = sp<BBinder>::make();
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 08fe071..e378b86 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -115,6 +115,14 @@
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null");
+ // TODO: allow all read and write operations
+ (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size());
+ FUZZ_LOG() << "setData done";
+ },
PARCEL_READ_NO_STATUS(size_t, allowFds),
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
@@ -353,6 +361,20 @@
FUZZ_LOG() << " status: " << status << " result: " << result;
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call hasBinders() with status";
+ bool result;
+ status_t status = p.hasBinders(&result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call hasBindersInRange() with status";
+ size_t offset = p.readUint32();
+ size_t length = p.readUint32();
+ bool result;
+ status_t status = p.hasBindersInRange(offset, length, &result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call compareDataInRange() with status";
size_t thisOffset = p.readUint32();
size_t otherOffset = p.readUint32();
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 394a000..50c05f4 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -265,7 +265,8 @@
auto vndkNamespace = android_get_exported_namespace("vndk");
if (!vndkNamespace) {
- return nullptr;
+ mDriverNamespace = nullptr;
+ return mDriverNamespace;
}
mDriverNamespace = android_create_namespace("updatable gfx driver",
@@ -617,7 +618,8 @@
auto vndkNamespace = android_get_exported_namespace("vndk");
if (!vndkNamespace) {
- return nullptr;
+ mAngleNamespace = nullptr;
+ return mAngleNamespace;
}
if (!linkDriverNamespaceLocked(mAngleNamespace, vndkNamespace, "")) {
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 9429d2c..86bf0ee 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -262,8 +262,6 @@
}
std::ostream& operator<<(std::ostream& out, const WindowInfo& info) {
- std::string transform;
- info.transform.dump(transform, "transform", " ");
out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId
<< ", inputConfig=" << info.inputConfig.string() << ", alpha=" << info.alpha << ", frame=["
<< info.frame.left << "," << info.frame.top << "][" << info.frame.right << ","
@@ -274,9 +272,11 @@
<< ", ownerUid=" << info.ownerUid.toString() << ", dispatchingTimeout="
<< std::chrono::duration_cast<std::chrono::milliseconds>(info.dispatchingTimeout).count()
<< "ms, token=" << info.token.get()
- << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n"
- << transform;
- if (info.canOccludePresentation) out << " canOccludePresentation";
+ << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode);
+ if (info.canOccludePresentation) out << ", canOccludePresentation";
+ std::string transform;
+ info.transform.dump(transform, "transform", " ");
+ out << "\n" << transform;
return out;
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 9e0ce1d..ff9d9a9 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -60,6 +60,45 @@
return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
}
+int32_t resolveActionForSplitMotionEvent(
+ int32_t action, int32_t flags, const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerProperties>& splitPointerProperties) {
+ LOG_ALWAYS_FATAL_IF(splitPointerProperties.empty());
+ const auto maskedAction = MotionEvent::getActionMasked(action);
+ if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN &&
+ maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) {
+ // The action is unaffected by splitting this motion event.
+ return action;
+ }
+ const auto actionIndex = MotionEvent::getActionIndex(action);
+ if (CC_UNLIKELY(actionIndex >= pointerProperties.size())) {
+ LOG(FATAL) << "Action index is out of bounds, index: " << actionIndex;
+ }
+
+ const auto affectedPointerId = pointerProperties[actionIndex].id;
+ std::optional<uint32_t> splitActionIndex;
+ for (uint32_t i = 0; i < splitPointerProperties.size(); i++) {
+ if (affectedPointerId == splitPointerProperties[i].id) {
+ splitActionIndex = i;
+ break;
+ }
+ }
+ if (!splitActionIndex.has_value()) {
+ // The affected pointer is not part of the split motion event.
+ return AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ if (splitPointerProperties.size() > 1) {
+ return maskedAction | (*splitActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ }
+
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ return ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) ? AMOTION_EVENT_ACTION_CANCEL
+ : AMOTION_EVENT_ACTION_UP;
+ }
+ return AMOTION_EVENT_ACTION_DOWN;
+}
+
} // namespace
const char* motionClassificationToString(MotionClassification classification) {
@@ -584,6 +623,28 @@
}
}
+void MotionEvent::splitFrom(const android::MotionEvent& other,
+ std::bitset<MAX_POINTER_ID + 1> splitPointerIds, int32_t newEventId) {
+ // TODO(b/327503168): The down time should be a parameter to the split function, because only
+ // the caller can know when the first event went down on the target.
+ const nsecs_t splitDownTime = other.mDownTime;
+
+ auto [action, pointerProperties, pointerCoords] =
+ split(other.getAction(), other.getFlags(), other.getHistorySize(),
+ other.mPointerProperties, other.mSamplePointerCoords, splitPointerIds);
+
+ // Initialize the event with zero pointers, and manually set the split pointers.
+ initialize(newEventId, other.mDeviceId, other.mSource, other.mDisplayId, /*hmac=*/{}, action,
+ other.mActionButton, other.mFlags, other.mEdgeFlags, other.mMetaState,
+ other.mButtonState, other.mClassification, other.mTransform, other.mXPrecision,
+ other.mYPrecision, other.mRawXCursorPosition, other.mRawYCursorPosition,
+ other.mRawTransform, splitDownTime, other.getEventTime(), /*pointerCount=*/0,
+ pointerProperties.data(), pointerCoords.data());
+ mPointerProperties = std::move(pointerProperties);
+ mSamplePointerCoords = std::move(pointerCoords);
+ mSampleEventTimes = other.mSampleEventTimes;
+}
+
void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
@@ -690,6 +751,18 @@
mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
}
+float MotionEvent::getRawXOffset() const {
+ // This is equivalent to the x-coordinate of the point that the origin of the raw coordinate
+ // space maps to.
+ return (mTransform * mRawTransform.inverse()).tx();
+}
+
+float MotionEvent::getRawYOffset() const {
+ // This is equivalent to the y-coordinate of the point that the origin of the raw coordinate
+ // space maps to.
+ return (mTransform * mRawTransform.inverse()).ty();
+}
+
void MotionEvent::scale(float globalScaleFactor) {
mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
@@ -934,6 +1007,45 @@
return android::base::StringPrintf("%" PRId32, action);
}
+std::tuple<int32_t, std::vector<PointerProperties>, std::vector<PointerCoords>> MotionEvent::split(
+ int32_t action, int32_t flags, int32_t historySize,
+ const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords,
+ std::bitset<MAX_POINTER_ID + 1> splitPointerIds) {
+ LOG_ALWAYS_FATAL_IF(!splitPointerIds.any());
+ const auto pointerCount = pointerProperties.size();
+ LOG_ALWAYS_FATAL_IF(pointerCoords.size() != (pointerCount * (historySize + 1)));
+ const auto splitCount = splitPointerIds.count();
+
+ std::vector<PointerProperties> splitPointerProperties;
+ std::vector<PointerCoords> splitPointerCoords;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ if (splitPointerIds.test(pointerProperties[i].id)) {
+ splitPointerProperties.emplace_back(pointerProperties[i]);
+ }
+ }
+ for (uint32_t i = 0; i < pointerCoords.size(); i++) {
+ if (splitPointerIds.test(pointerProperties[i % pointerCount].id)) {
+ splitPointerCoords.emplace_back(pointerCoords[i]);
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(splitPointerCoords.size() !=
+ (splitPointerProperties.size() * (historySize + 1)));
+
+ if (CC_UNLIKELY(splitPointerProperties.size() != splitCount)) {
+ LOG(FATAL) << "Cannot split MotionEvent: Requested splitting " << splitCount
+ << " pointers from the original event, but the original event only contained "
+ << splitPointerProperties.size() << " of those pointers.";
+ }
+
+ // TODO(b/327503168): Verify the splitDownTime here once it is used correctly.
+
+ const auto splitAction = resolveActionForSplitMotionEvent(action, flags, pointerProperties,
+ splitPointerProperties);
+ return {splitAction, splitPointerProperties, splitPointerCoords};
+}
+
// Apply the given transformation to the point without checking whether the entire transform
// should be disregarded altogether for the provided source.
static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
index 0412d08..6872af2 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -113,7 +113,12 @@
// Adds new predictions to mRecentPredictions and maintains the invariant that elements are
// sorted in ascending order of targetTimestamp.
void MotionPredictorMetricsManager::onPredict(const MotionEvent& predictionEvent) {
- for (size_t i = 0; i < predictionEvent.getHistorySize() + 1; ++i) {
+ const size_t numPredictions = predictionEvent.getHistorySize() + 1;
+ if (numPredictions > mMaxNumPredictions) {
+ LOG(WARNING) << "numPredictions (" << numPredictions << ") > mMaxNumPredictions ("
+ << mMaxNumPredictions << "). Ignoring extra predictions in metrics.";
+ }
+ for (size_t i = 0; (i < numPredictions) && (i < mMaxNumPredictions); ++i) {
// Convert MotionEvent to PredictionPoint.
const PointerCoords* coords =
predictionEvent.getHistoricalRawPointerCoords(/*pointerIndex=*/0, i);
@@ -325,42 +330,44 @@
mAtomFields[i].highVelocityOffTrajectoryRmse =
static_cast<int>(offTrajectoryRmse * 1000);
}
+ }
- // Scale-invariant errors: reported only for the last time bucket, where the values
- // represent an average across all time buckets.
- if (i + 1 == mMaxNumPredictions) {
- // Compute error averages.
- float alongTrajectoryRmseSum = 0;
- float offTrajectoryRmseSum = 0;
- for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
- // If we have general errors (checked above), we should always also have
- // scale-invariant errors.
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantErrorsCount == 0,
- "mAggregatedMetrics[%zu].scaleInvariantErrorsCount is 0", j);
-
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
- "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
- "should not be negative",
- j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
- alongTrajectoryRmseSum +=
- std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
- mAggregatedMetrics[j].scaleInvariantErrorsCount);
-
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
- "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
- "should not be negative",
- j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
- offTrajectoryRmseSum +=
- std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
- mAggregatedMetrics[j].scaleInvariantErrorsCount);
+ // Scale-invariant errors: the average scale-invariant error across all time buckets
+ // is reported in the last time bucket.
+ {
+ // Compute error averages.
+ float alongTrajectoryRmseSum = 0;
+ float offTrajectoryRmseSum = 0;
+ int bucket_count = 0;
+ for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
+ if (mAggregatedMetrics[j].scaleInvariantErrorsCount == 0) {
+ continue;
}
- const float averageAlongTrajectoryRmse =
- alongTrajectoryRmseSum / mAggregatedMetrics.size();
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
+ alongTrajectoryRmseSum +=
+ std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
+ offTrajectoryRmseSum += std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ ++bucket_count;
+ }
+
+ if (bucket_count > 0) {
+ const float averageAlongTrajectoryRmse = alongTrajectoryRmseSum / bucket_count;
mAtomFields.back().scaleInvariantAlongTrajectoryRmse =
static_cast<int>(averageAlongTrajectoryRmse * 1000);
- const float averageOffTrajectoryRmse = offTrajectoryRmseSum / mAggregatedMetrics.size();
+ const float averageOffTrajectoryRmse = offTrajectoryRmseSum / bucket_count;
mAtomFields.back().scaleInvariantOffTrajectoryRmse =
static_cast<int>(averageOffTrajectoryRmse * 1000);
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a965573..0df06b7 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include <gui/constants.h>
#include <input/Input.h>
+#include <input/InputEventBuilders.h>
namespace android {
@@ -31,6 +32,18 @@
static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+static constexpr auto POINTER_0_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+static constexpr auto POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+static constexpr auto POINTER_0_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+static constexpr auto POINTER_1_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
class BaseTest : public testing::Test {
protected:
static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
@@ -358,8 +371,10 @@
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
EXPECT_EQ(mTransform, event->getTransform());
- ASSERT_EQ(X_OFFSET, event->getXOffset());
- ASSERT_EQ(Y_OFFSET, event->getYOffset());
+ ASSERT_NEAR((-RAW_X_OFFSET / RAW_X_SCALE) * X_SCALE + X_OFFSET, event->getRawXOffset(),
+ EPSILON);
+ ASSERT_NEAR((-RAW_Y_OFFSET / RAW_Y_SCALE) * Y_SCALE + Y_OFFSET, event->getRawYOffset(),
+ EPSILON);
ASSERT_EQ(2.0f, event->getXPrecision());
ASSERT_EQ(2.1f, event->getYPrecision());
ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
@@ -554,25 +569,168 @@
ASSERT_EQ(event.getX(0), copy.getX(0));
}
+TEST_F(MotionEventTest, SplitPointerDown) {
+ MotionEvent event = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .build();
+
+ MotionEvent splitDown;
+ std::bitset<MAX_POINTER_ID + 1> splitDownIds{};
+ splitDownIds.set(6, true);
+ splitDown.splitFrom(event, splitDownIds, /*eventId=*/42);
+ ASSERT_EQ(splitDown.getAction(), AMOTION_EVENT_ACTION_DOWN);
+ ASSERT_EQ(splitDown.getPointerCount(), 1u);
+ ASSERT_EQ(splitDown.getPointerId(0), 6);
+ ASSERT_EQ(splitDown.getX(0), 6);
+ ASSERT_EQ(splitDown.getY(0), 6);
+
+ MotionEvent splitPointerDown;
+ std::bitset<MAX_POINTER_ID + 1> splitPointerDownIds{};
+ splitPointerDownIds.set(6, true);
+ splitPointerDownIds.set(8, true);
+ splitPointerDown.splitFrom(event, splitPointerDownIds, /*eventId=*/42);
+ ASSERT_EQ(splitPointerDown.getAction(), POINTER_0_DOWN);
+ ASSERT_EQ(splitPointerDown.getPointerCount(), 2u);
+ ASSERT_EQ(splitPointerDown.getPointerId(0), 6);
+ ASSERT_EQ(splitPointerDown.getX(0), 6);
+ ASSERT_EQ(splitPointerDown.getY(0), 6);
+ ASSERT_EQ(splitPointerDown.getPointerId(1), 8);
+ ASSERT_EQ(splitPointerDown.getX(1), 8);
+ ASSERT_EQ(splitPointerDown.getY(1), 8);
+
+ MotionEvent splitMove;
+ std::bitset<MAX_POINTER_ID + 1> splitMoveIds{};
+ splitMoveIds.set(4, true);
+ splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43);
+ ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE);
+ ASSERT_EQ(splitMove.getPointerCount(), 1u);
+ ASSERT_EQ(splitMove.getPointerId(0), 4);
+ ASSERT_EQ(splitMove.getX(0), 4);
+ ASSERT_EQ(splitMove.getY(0), 4);
+}
+
+TEST_F(MotionEventTest, SplitPointerUp) {
+ MotionEvent event = MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .build();
+
+ MotionEvent splitUp;
+ std::bitset<MAX_POINTER_ID + 1> splitUpIds{};
+ splitUpIds.set(4, true);
+ splitUp.splitFrom(event, splitUpIds, /*eventId=*/42);
+ ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_UP);
+ ASSERT_EQ(splitUp.getPointerCount(), 1u);
+ ASSERT_EQ(splitUp.getPointerId(0), 4);
+ ASSERT_EQ(splitUp.getX(0), 4);
+ ASSERT_EQ(splitUp.getY(0), 4);
+
+ MotionEvent splitPointerUp;
+ std::bitset<MAX_POINTER_ID + 1> splitPointerUpIds{};
+ splitPointerUpIds.set(4, true);
+ splitPointerUpIds.set(8, true);
+ splitPointerUp.splitFrom(event, splitPointerUpIds, /*eventId=*/42);
+ ASSERT_EQ(splitPointerUp.getAction(), POINTER_0_UP);
+ ASSERT_EQ(splitPointerUp.getPointerCount(), 2u);
+ ASSERT_EQ(splitPointerUp.getPointerId(0), 4);
+ ASSERT_EQ(splitPointerUp.getX(0), 4);
+ ASSERT_EQ(splitPointerUp.getY(0), 4);
+ ASSERT_EQ(splitPointerUp.getPointerId(1), 8);
+ ASSERT_EQ(splitPointerUp.getX(1), 8);
+ ASSERT_EQ(splitPointerUp.getY(1), 8);
+
+ MotionEvent splitMove;
+ std::bitset<MAX_POINTER_ID + 1> splitMoveIds{};
+ splitMoveIds.set(6, true);
+ splitMoveIds.set(8, true);
+ splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43);
+ ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE);
+ ASSERT_EQ(splitMove.getPointerCount(), 2u);
+ ASSERT_EQ(splitMove.getPointerId(0), 6);
+ ASSERT_EQ(splitMove.getX(0), 6);
+ ASSERT_EQ(splitMove.getY(0), 6);
+ ASSERT_EQ(splitMove.getPointerId(1), 8);
+ ASSERT_EQ(splitMove.getX(1), 8);
+ ASSERT_EQ(splitMove.getY(1), 8);
+}
+
+TEST_F(MotionEventTest, SplitPointerUpCancel) {
+ MotionEvent event = MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .addFlag(AMOTION_EVENT_FLAG_CANCELED)
+ .build();
+
+ MotionEvent splitUp;
+ std::bitset<MAX_POINTER_ID + 1> splitUpIds{};
+ splitUpIds.set(6, true);
+ splitUp.splitFrom(event, splitUpIds, /*eventId=*/42);
+ ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_CANCEL);
+ ASSERT_EQ(splitUp.getPointerCount(), 1u);
+ ASSERT_EQ(splitUp.getPointerId(0), 6);
+ ASSERT_EQ(splitUp.getX(0), 6);
+ ASSERT_EQ(splitUp.getY(0), 6);
+}
+
+TEST_F(MotionEventTest, SplitPointerMove) {
+ MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .build();
+
+ MotionEvent splitMove;
+ std::bitset<MAX_POINTER_ID + 1> splitMoveIds{};
+ splitMoveIds.set(4, true);
+ splitMoveIds.set(8, true);
+ splitMove.splitFrom(event, splitMoveIds, /*eventId=*/42);
+ ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE);
+ ASSERT_EQ(splitMove.getPointerCount(), 2u);
+ ASSERT_EQ(splitMove.getPointerId(0), 4);
+ ASSERT_EQ(splitMove.getX(0), event.getX(0));
+ ASSERT_EQ(splitMove.getY(0), event.getY(0));
+ ASSERT_EQ(splitMove.getRawX(0), event.getRawX(0));
+ ASSERT_EQ(splitMove.getRawY(0), event.getRawY(0));
+ ASSERT_EQ(splitMove.getPointerId(1), 8);
+ ASSERT_EQ(splitMove.getX(1), event.getX(2));
+ ASSERT_EQ(splitMove.getY(1), event.getY(2));
+ ASSERT_EQ(splitMove.getRawX(1), event.getRawX(2));
+ ASSERT_EQ(splitMove.getRawY(1), event.getRawY(2));
+}
+
TEST_F(MotionEventTest, OffsetLocation) {
MotionEvent event;
initializeEventWithHistory(&event);
+ const float xOffset = event.getRawXOffset();
+ const float yOffset = event.getRawYOffset();
event.offsetLocation(5.0f, -2.0f);
- ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
- ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
+ ASSERT_EQ(xOffset + 5.0f, event.getRawXOffset());
+ ASSERT_EQ(yOffset - 2.0f, event.getRawYOffset());
}
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
const float unscaledOrientation = event.getOrientation(0);
+ const float unscaledXOffset = event.getRawXOffset();
+ const float unscaledYOffset = event.getRawYOffset();
event.scale(2.0f);
- ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
- ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
+ ASSERT_EQ(unscaledXOffset * 2, event.getRawXOffset());
+ ASSERT_EQ(unscaledYOffset * 2, event.getRawYOffset());
ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index f9d0bda..b5fab49 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -133,8 +133,10 @@
EXPECT_EQ(args.buttonState, motionEvent.getButtonState());
EXPECT_EQ(args.classification, motionEvent.getClassification());
EXPECT_EQ(args.transform, motionEvent.getTransform());
- EXPECT_EQ(args.xOffset, motionEvent.getXOffset());
- EXPECT_EQ(args.yOffset, motionEvent.getYOffset());
+ EXPECT_NEAR((-args.rawXOffset / args.rawXScale) * args.xScale + args.xOffset,
+ motionEvent.getRawXOffset(), EPSILON);
+ EXPECT_NEAR((-args.rawYOffset / args.rawYScale) * args.yScale + args.yOffset,
+ motionEvent.getRawYOffset(), EPSILON);
EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision());
EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision());
EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON);
diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
index 31cc145..cc41eeb 100644
--- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -238,14 +238,17 @@
// --- Ground-truth-generation helper functions. ---
+// Generates numPoints ground truth points with values equal to those of the given
+// GroundTruthPoint, and with consecutive timestamps separated by the given inputInterval.
std::vector<GroundTruthPoint> generateConstantGroundTruthPoints(
- const GroundTruthPoint& groundTruthPoint, size_t numPoints) {
+ const GroundTruthPoint& groundTruthPoint, size_t numPoints,
+ nsecs_t inputInterval = TEST_PREDICTION_INTERVAL_NANOS) {
std::vector<GroundTruthPoint> groundTruthPoints;
nsecs_t timestamp = groundTruthPoint.timestamp;
for (size_t i = 0; i < numPoints; ++i) {
groundTruthPoints.emplace_back(groundTruthPoint);
groundTruthPoints.back().timestamp = timestamp;
- timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ timestamp += inputInterval;
}
return groundTruthPoints;
}
@@ -280,7 +283,8 @@
const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
.timestamp = TEST_INITIAL_TIMESTAMP};
const std::vector<GroundTruthPoint> groundTruthPoints =
- generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3);
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3,
+ /*inputInterval=*/10);
ASSERT_EQ(3u, groundTruthPoints.size());
// First point.
@@ -290,11 +294,11 @@
// Second point.
EXPECT_EQ(groundTruthPoints[1].position, groundTruthPoint.position);
EXPECT_EQ(groundTruthPoints[1].pressure, groundTruthPoint.pressure);
- EXPECT_GT(groundTruthPoints[1].timestamp, groundTruthPoints[0].timestamp);
+ EXPECT_EQ(groundTruthPoints[1].timestamp, groundTruthPoint.timestamp + 10);
// Third point.
EXPECT_EQ(groundTruthPoints[2].position, groundTruthPoint.position);
EXPECT_EQ(groundTruthPoints[2].pressure, groundTruthPoint.pressure);
- EXPECT_GT(groundTruthPoints[2].timestamp, groundTruthPoints[1].timestamp);
+ EXPECT_EQ(groundTruthPoints[2].timestamp, groundTruthPoint.timestamp + 20);
}
TEST(GenerateCircularArcGroundTruthTest, StraightLineUpwards) {
@@ -333,16 +337,19 @@
// --- Prediction-generation helper functions. ---
-// Creates a sequence of predictions with values equal to those of the given GroundTruthPoint.
-std::vector<PredictionPoint> generateConstantPredictions(const GroundTruthPoint& groundTruthPoint) {
+// Generates TEST_MAX_NUM_PREDICTIONS predictions with values equal to those of the given
+// GroundTruthPoint, and with consecutive timestamps separated by the given predictionInterval.
+std::vector<PredictionPoint> generateConstantPredictions(
+ const GroundTruthPoint& groundTruthPoint,
+ nsecs_t predictionInterval = TEST_PREDICTION_INTERVAL_NANOS) {
std::vector<PredictionPoint> predictions;
- nsecs_t predictionTimestamp = groundTruthPoint.timestamp + TEST_PREDICTION_INTERVAL_NANOS;
+ nsecs_t predictionTimestamp = groundTruthPoint.timestamp + predictionInterval;
for (size_t j = 0; j < TEST_MAX_NUM_PREDICTIONS; ++j) {
predictions.push_back(PredictionPoint{{.position = groundTruthPoint.position,
.pressure = groundTruthPoint.pressure},
.originTimestamp = groundTruthPoint.timestamp,
.targetTimestamp = predictionTimestamp});
- predictionTimestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ predictionTimestamp += predictionInterval;
}
return predictions;
}
@@ -375,8 +382,9 @@
TEST(GeneratePredictionsTest, GenerateConstantPredictions) {
const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
.timestamp = TEST_INITIAL_TIMESTAMP};
+ const nsecs_t predictionInterval = 10;
const std::vector<PredictionPoint> predictionPoints =
- generateConstantPredictions(groundTruthPoint);
+ generateConstantPredictions(groundTruthPoint, predictionInterval);
ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, predictionPoints.size());
for (size_t i = 0; i < predictionPoints.size(); ++i) {
@@ -385,8 +393,7 @@
EXPECT_THAT(predictionPoints[i].pressure, FloatNear(groundTruthPoint.pressure, 1e-6));
EXPECT_EQ(predictionPoints[i].originTimestamp, groundTruthPoint.timestamp);
EXPECT_EQ(predictionPoints[i].targetTimestamp,
- groundTruthPoint.timestamp +
- static_cast<nsecs_t>(i + 1) * TEST_PREDICTION_INTERVAL_NANOS);
+ TEST_INITIAL_TIMESTAMP + static_cast<nsecs_t>(i + 1) * predictionInterval);
}
}
@@ -678,12 +685,9 @@
// • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements.
// • predictionPoints: the first index points to a vector of predictions corresponding to the
// source ground truth point with the same index.
-// - The first element should be empty, because there are not expected to be predictions until
-// we have received 2 ground truth points.
-// - The last element may be empty, because there will be no future ground truth points to
-// associate with those predictions (if not empty, it will be ignored).
+// - For empty prediction vectors, MetricsManager::onPredict will not be called.
// - To test all prediction buckets, there should be at least TEST_MAX_NUM_PREDICTIONS non-empty
-// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and
+// prediction vectors (that is, excluding the first and last). Thus, groundTruthPoints and
// predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2.
//
// When the function returns, outReportedAtomFields will contain the reported AtomFields.
@@ -697,19 +701,12 @@
createMockReportAtomFunction(
outReportedAtomFields));
- // Validate structure of groundTruthPoints and predictionPoints.
- ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
ASSERT_GE(groundTruthPoints.size(), 2u);
- ASSERT_EQ(predictionPoints[0].size(), 0u);
- for (size_t i = 1; i + 1 < predictionPoints.size(); ++i) {
- SCOPED_TRACE(testing::Message() << "i = " << i);
- ASSERT_EQ(predictionPoints[i].size(), TEST_MAX_NUM_PREDICTIONS);
- }
+ ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
- // Pass ground truth points and predictions (for all except first and last ground truth).
for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
metricsManager.onRecord(makeMotionEvent(groundTruthPoints[i]));
- if ((i > 0) && (i + 1 < predictionPoints.size())) {
+ if (!predictionPoints[i].empty()) {
metricsManager.onPredict(makeMotionEvent(predictionPoints[i]));
}
}
@@ -738,7 +735,7 @@
// Perfect predictions test:
// • Input: constant input events, perfect predictions matching the input events.
// • Expectation: all error metrics should be zero, or NO_DATA_SENTINEL for "unreported" metrics.
-// (For example, scale-invariant errors are only reported for the final time bucket.)
+// (For example, scale-invariant errors are only reported for the last time bucket.)
TEST(MotionPredictorMetricsManagerTest, ConstantGroundTruthPerfectPredictions) {
GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10.0f, 20.0f), .pressure = 0.6f},
.timestamp = TEST_INITIAL_TIMESTAMP};
@@ -977,5 +974,35 @@
}
}
+// Robustness test:
+// • Input: input events separated by a significantly greater time interval than the interval
+// between predictions.
+// • Expectation: the MetricsManager should not crash in this case. (No assertions are made about
+// the resulting metrics.)
+//
+// In practice, this scenario could arise either if the input and prediction intervals are
+// mismatched, or if input events are missing (dropped or skipped for some reason).
+TEST(MotionPredictorMetricsManagerTest, MismatchedInputAndPredictionInterval) {
+ // Create two ground truth points separated by MAX_NUM_PREDICTIONS * PREDICTION_INTERVAL,
+ // so that the second ground truth point corresponds to the last prediction bucket. This
+ // ensures that the scale-invariant error codepath will be run, giving full code coverage.
+ GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(0.0f, 0.0f), .pressure = 0.5f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+ const nsecs_t inputInterval = TEST_MAX_NUM_PREDICTIONS * TEST_PREDICTION_INTERVAL_NANOS;
+ const std::vector<GroundTruthPoint> groundTruthPoints =
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/2, inputInterval);
+
+ // Create predictions separated by the prediction interval.
+ std::vector<std::vector<PredictionPoint>> predictionPoints;
+ for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
+ predictionPoints.push_back(
+ generateConstantPredictions(groundTruthPoints[i], TEST_PREDICTION_INTERVAL_NANOS));
+ }
+
+ // Test that we can run the MetricsManager without crashing.
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
+}
+
} // namespace
} // namespace android
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
index 6119ba7..c630d97 100644
--- a/libs/math/include/math/mat4.h
+++ b/libs/math/include/math/mat4.h
@@ -34,6 +34,14 @@
#define CONSTEXPR
#endif
+#ifdef _WIN32
+// windows.h contains obsolete defines of 'near' and 'far' for systems using
+// legacy 16 bit pointers. Undefine them to avoid conflicting with the usage of
+// 'near' and 'far' in this file.
+#undef near
+#undef far
+#endif
+
namespace android {
// -------------------------------------------------------------------------------------
namespace details {
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index f7f20b4..03f4f39 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -34,7 +34,13 @@
cc_library_headers {
name: "libnativedisplay_headers",
+ host_supported: true,
export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
}
cc_library_shared {
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 0e90327..8558074 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -54,6 +54,11 @@
"test_com.android.media.swcodec",
],
host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
}
ndk_library {
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index e0e30c3..6fcb3a4 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -52,6 +52,10 @@
#include <inttypes.h>
#include <sys/cdefs.h>
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
__BEGIN_DECLS
// clang-format off
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index eb7a9d5..feb76a1 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -25,6 +25,7 @@
#include <GrContextOptions.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <android-base/stringprintf.h>
@@ -766,8 +767,7 @@
base::unique_fd fenceDup(dupedFd);
VkSemaphore waitSemaphore =
getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
- GrBackendSemaphore beSemaphore;
- beSemaphore.initVulkan(waitSemaphore);
+ GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore);
grContext->wait(1, &beSemaphore, true /* delete after wait */);
}
@@ -775,8 +775,7 @@
VulkanInterface& vi = getVulkanInterface(isProtected());
VkSemaphore semaphore = vi.createExportableSemaphore();
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
+ GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore);
GrFlushInfo flushInfo;
DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp
index 1e7e707..00514c4 100644
--- a/libs/sensorprivacy/Android.bp
+++ b/libs/sensorprivacy/Android.bp
@@ -57,7 +57,6 @@
filegroup {
name: "libsensorprivacy_aidl",
srcs: [
- "aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl",
"aidl/android/hardware/ISensorPrivacyListener.aidl",
"aidl/android/hardware/ISensorPrivacyManager.aidl",
],
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index fe93786..3f3ad93 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -155,10 +155,9 @@
return DISABLED;
}
-std::vector<hardware::CameraPrivacyAllowlistEntry>
- SensorPrivacyManager::getCameraPrivacyAllowlist(){
+std::vector<String16> SensorPrivacyManager::getCameraPrivacyAllowlist(){
sp<hardware::ISensorPrivacyManager> service = getService();
- std::vector<hardware::CameraPrivacyAllowlistEntry> result;
+ std::vector<String16> result;
if (service != nullptr) {
service->getCameraPrivacyAllowlist(&result);
return result;
diff --git a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl
deleted file mode 100644
index 03e1537..0000000
--- a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware;
-
-parcelable CameraPrivacyAllowlistEntry {
- String packageName;
- boolean isMandatory;
-}
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index b6bd39e..f707187 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -16,7 +16,6 @@
package android.hardware;
-import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
/** @hide */
@@ -43,7 +42,7 @@
void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
- List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ List<String> getCameraPrivacyAllowlist();
int getToggleSensorPrivacyState(int toggleType, int sensor);
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 9e97e16..8935b76 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -45,9 +45,7 @@
enum {
ENABLED = 1,
DISABLED = 2,
- AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = 3,
- AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = 4,
- AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = 5
+ ENABLED_EXCEPT_ALLOWLISTED_APPS = 3
};
SensorPrivacyManager();
@@ -62,7 +60,7 @@
bool isToggleSensorPrivacyEnabled(int toggleType, int sensor);
status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result);
int getToggleSensorPrivacyState(int toggleType, int sensor);
- std::vector<hardware::CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ std::vector<String16> getCameraPrivacyAllowlist();
bool isCameraPrivacyEnabled(String16 packageName);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 654c903..f697f94 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -34,9 +34,9 @@
BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
- BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY
- BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
- BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
+ BATTERY_PROP_MANUFACTURING_DATE = 7, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
+ BATTERY_PROP_FIRST_USAGE_DATE = 8, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
+ BATTERY_PROP_CHARGING_POLICY = 9, // equals BATTERY_PROPERTY_CHARGING_POLICY
BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH
BATTERY_PROP_PART_STATUS = 12, // equals BATTERY_PROPERTY_PART_STATUS
};
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 3ac4285..9db3574 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -26,6 +26,7 @@
namespace android {
namespace {
+
bool isFromMouse(const NotifyMotionArgs& args) {
return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
args.pointerProperties[0].toolType == ToolType::MOUSE;
@@ -44,13 +45,23 @@
bool isStylusHoverEvent(const NotifyMotionArgs& args) {
return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
}
+
+inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPoint>> change,
+ PointerChoreographerPolicyInterface& policy) {
+ if (!change) {
+ return;
+ }
+ const auto& [displayId, cursorPosition] = *change;
+ policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
+}
+
} // namespace
// --- PointerChoreographer ---
PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
PointerChoreographerPolicyInterface& policy)
- : mTouchControllerConstructor([this]() REQUIRES(mLock) {
+ : mTouchControllerConstructor([this]() {
return mPolicy.createPointerController(
PointerControllerInterface::ControllerType::TOUCH);
}),
@@ -62,10 +73,16 @@
mStylusPointerIconEnabled(false) {}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
- std::scoped_lock _l(mLock);
+ PointerDisplayChange pointerDisplayChange;
- mInputDeviceInfos = args.inputDeviceInfos;
- updatePointerControllersLocked();
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ mInputDeviceInfos = args.inputDeviceInfos;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
mNextListener.notify(args);
}
@@ -104,7 +121,7 @@
<< args.dump();
}
- auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+ auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
@@ -124,7 +141,7 @@
}
NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
- auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+ auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
NotifyMotionArgs newArgs(args);
newArgs.displayId = displayId;
@@ -308,17 +325,13 @@
return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
}
-std::pair<int32_t, PointerControllerInterface&>
-PointerChoreographer::getDisplayIdAndMouseControllerLocked(int32_t associatedDisplayId) {
+std::pair<int32_t, PointerControllerInterface&> PointerChoreographer::ensureMouseControllerLocked(
+ int32_t associatedDisplayId) {
const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
- // Get the mouse pointer controller for the display, or create one if it doesn't exist.
- auto [it, emplaced] =
- mMousePointersByDisplay.try_emplace(displayId,
- getMouseControllerConstructor(displayId));
- if (emplaced) {
- notifyPointerDisplayIdChangedLocked();
- }
+ auto it = mMousePointersByDisplay.find(displayId);
+ LOG_ALWAYS_FATAL_IF(it == mMousePointersByDisplay.end(),
+ "There is no mouse controller created for display %d", displayId);
return {displayId, *it->second};
}
@@ -333,7 +346,7 @@
return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
}
-void PointerChoreographer::updatePointerControllersLocked() {
+PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
std::set<DeviceId> touchDevicesToKeep;
std::set<DeviceId> stylusDevicesToKeep;
@@ -382,11 +395,12 @@
mInputDeviceInfos.end();
});
- // Notify the policy if there's a change on the pointer display ID.
- notifyPointerDisplayIdChangedLocked();
+ // Check if we need to notify the policy if there's a change on the pointer display ID.
+ return calculatePointerDisplayChangeToNotify();
}
-void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
+PointerChoreographer::PointerDisplayChange
+PointerChoreographer::calculatePointerDisplayChangeToNotify() {
int32_t displayIdToNotify = ADISPLAY_ID_NONE;
FloatPoint cursorPosition = {0, 0};
if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
@@ -398,38 +412,49 @@
displayIdToNotify = pointerController->getDisplayId();
cursorPosition = pointerController->getPosition();
}
-
if (mNotifiedPointerDisplayId == displayIdToNotify) {
- return;
+ return {};
}
- mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
mNotifiedPointerDisplayId = displayIdToNotify;
+ return {{displayIdToNotify, cursorPosition}};
}
void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
- std::scoped_lock _l(mLock);
+ PointerDisplayChange pointerDisplayChange;
- mDefaultMouseDisplayId = displayId;
- updatePointerControllersLocked();
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ mDefaultMouseDisplayId = displayId;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
- std::scoped_lock _l(mLock);
- for (const auto& viewport : viewports) {
- const int32_t displayId = viewport.displayId;
- if (const auto it = mMousePointersByDisplay.find(displayId);
- it != mMousePointersByDisplay.end()) {
- it->second->setDisplayViewport(viewport);
- }
- for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
- const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
- if (info && info->getAssociatedDisplayId() == displayId) {
- stylusPointerController->setDisplayViewport(viewport);
+ PointerDisplayChange pointerDisplayChange;
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ for (const auto& viewport : viewports) {
+ const int32_t displayId = viewport.displayId;
+ if (const auto it = mMousePointersByDisplay.find(displayId);
+ it != mMousePointersByDisplay.end()) {
+ it->second->setDisplayViewport(viewport);
+ }
+ for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+ const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+ if (info && info->getAssociatedDisplayId() == displayId) {
+ stylusPointerController->setDisplayViewport(viewport);
+ }
}
}
- }
- mViewports = viewports;
- notifyPointerDisplayIdChangedLocked();
+ mViewports = viewports;
+ pointerDisplayChange = calculatePointerDisplayChangeToNotify();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
@@ -453,21 +478,33 @@
}
void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
- std::scoped_lock _l(mLock);
- if (mShowTouchesEnabled == enabled) {
- return;
- }
- mShowTouchesEnabled = enabled;
- updatePointerControllersLocked();
+ PointerDisplayChange pointerDisplayChange;
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ if (mShowTouchesEnabled == enabled) {
+ return;
+ }
+ mShowTouchesEnabled = enabled;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
- std::scoped_lock _l(mLock);
- if (mStylusPointerIconEnabled == enabled) {
- return;
- }
- mStylusPointerIconEnabled = enabled;
- updatePointerControllersLocked();
+ PointerDisplayChange pointerDisplayChange;
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ if (mStylusPointerIconEnabled == enabled) {
+ return;
+ }
+ mStylusPointerIconEnabled = enabled;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
bool PointerChoreographer::setPointerIcon(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 6aab3aa..db1488b 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -109,11 +109,13 @@
void dump(std::string& dump) override;
private:
- void updatePointerControllersLocked() REQUIRES(mLock);
- void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
+ using PointerDisplayChange =
+ std::optional<std::tuple<int32_t /*displayId*/, FloatPoint /*cursorPosition*/>>;
+ [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(mLock);
+ [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(mLock);
const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
- std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked(
+ std::pair<int32_t /*displayId*/, PointerControllerInterface&> ensureMouseControllerLocked(
int32_t associatedDisplayId) REQUIRES(mLock);
InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
bool canUnfadeOnDisplay(int32_t displayId) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8858f0c..10e0a36 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -103,6 +103,26 @@
}
}
+// Helper to get a trace tracker from a traced key or motion entry.
+const std::unique_ptr<trace::EventTrackerInterface>& getTraceTracker(const EventEntry& entry) {
+ switch (entry.type) {
+ case EventEntry::Type::MOTION: {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ ensureEventTraced(motion);
+ return motion.traceTracker;
+ }
+ case EventEntry::Type::KEY: {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ ensureEventTraced(key);
+ return key.traceTracker;
+ }
+ default: {
+ const static std::unique_ptr<trace::EventTrackerInterface> kNullTracker;
+ return kNullTracker;
+ }
+ }
+}
+
// Temporarily releases a held mutex for the lifetime of the instance.
// Named to match std::scoped_lock
class scoped_unlock {
@@ -656,13 +676,13 @@
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
const TouchState& newTouchState,
const MotionEntry& entry) {
- std::vector<TouchedWindow> out;
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
// ACTION_SCROLL events should not affect the hovering pointer dispatch
return {};
}
+ std::vector<TouchedWindow> out;
// We should consider all hovering pointers here. But for now, just use the first one
const PointerProperties& pointer = entry.pointerProperties[0];
@@ -1147,10 +1167,6 @@
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
- if (done && mTracer) {
- ensureEventTraced(*keyEntry);
- mTracer->eventProcessingComplete(*keyEntry->traceTracker);
- }
break;
}
@@ -1176,10 +1192,6 @@
}
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
- if (done && mTracer) {
- ensureEventTraced(*motionEntry);
- mTracer->eventProcessingComplete(*motionEntry->traceTracker);
- }
break;
}
@@ -1205,6 +1217,12 @@
}
mLastDropReason = dropReason;
+ if (mTracer) {
+ if (auto& traceTracker = getTraceTracker(*mPendingEvent); traceTracker != nullptr) {
+ mTracer->eventProcessingComplete(*traceTracker);
+ }
+ }
+
releasePendingEventLocked();
nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
}
@@ -2558,9 +2576,9 @@
return {};
}
- // Drop touch events if requested by input feature
+ // Do not slide events to the window which can not receive motion event
if (newTouchedWindowHandle != nullptr &&
- shouldDropInput(entry, newTouchedWindowHandle)) {
+ !canWindowReceiveMotionLocked(newTouchedWindowHandle, entry)) {
newTouchedWindowHandle = nullptr;
}
@@ -2633,19 +2651,14 @@
{
std::vector<TouchedWindow> hoveringWindows =
getHoveringWindowsLocked(oldState, tempTouchState, entry);
+ // Hardcode to single hovering pointer for now.
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(entry.pointerProperties[0].id);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
- std::optional<InputTarget> target =
- createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
- touchedWindow.targetFlags,
- touchedWindow.getDownTimeInTarget(entry.deviceId));
- if (!target) {
- continue;
- }
- // Hardcode to single hovering pointer for now.
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(entry.pointerProperties[0].id);
- target->addPointers(pointerIds, touchedWindow.windowHandle->getInfo()->transform);
- targets.push_back(*target);
+ addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ targets);
}
}
@@ -4268,72 +4281,13 @@
std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds,
nsecs_t splitDownTime) {
- ALOG_ASSERT(pointerIds.any());
+ const auto& [action, pointerProperties, pointerCoords] =
+ MotionEvent::split(originalMotionEntry.action, originalMotionEntry.flags,
+ /*historySize=*/0, originalMotionEntry.pointerProperties,
+ originalMotionEntry.pointerCoords, pointerIds);
- uint32_t splitPointerIndexMap[MAX_POINTERS];
- std::vector<PointerProperties> splitPointerProperties;
- std::vector<PointerCoords> splitPointerCoords;
-
- uint32_t originalPointerCount = originalMotionEntry.getPointerCount();
- uint32_t splitPointerCount = 0;
-
- for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
- originalPointerIndex++) {
- const PointerProperties& pointerProperties =
- originalMotionEntry.pointerProperties[originalPointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.test(pointerId)) {
- splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
- splitPointerProperties.push_back(pointerProperties);
- splitPointerCoords.push_back(originalMotionEntry.pointerCoords[originalPointerIndex]);
- splitPointerCount += 1;
- }
- }
-
- if (splitPointerCount != pointerIds.count()) {
- // This is bad. We are missing some of the pointers that we expected to deliver.
- // Most likely this indicates that we received an ACTION_MOVE events that has
- // different pointer ids than we expected based on the previous ACTION_DOWN
- // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
- // in this way.
- ALOGW("Dropping split motion event because the pointer count is %d but "
- "we expected there to be %zu pointers. This probably means we received "
- "a broken sequence of pointer ids from the input device: %s",
- splitPointerCount, pointerIds.count(), originalMotionEntry.getDescription().c_str());
- return nullptr;
- }
-
- int32_t action = originalMotionEntry.action;
- int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
- if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
- maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- int32_t originalPointerIndex = MotionEvent::getActionIndex(action);
- const PointerProperties& pointerProperties =
- originalMotionEntry.pointerProperties[originalPointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.test(pointerId)) {
- if (pointerIds.count() == 1) {
- // The first/last pointer went down/up.
- action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
- ? AMOTION_EVENT_ACTION_DOWN
- : (originalMotionEntry.flags & AMOTION_EVENT_FLAG_CANCELED) != 0
- ? AMOTION_EVENT_ACTION_CANCEL
- : AMOTION_EVENT_ACTION_UP;
- } else {
- // A secondary pointer went down/up.
- uint32_t splitPointerIndex = 0;
- while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) {
- splitPointerIndex += 1;
- }
- action = maskedAction |
- (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- // An unrelated pointer changed.
- action = AMOTION_EVENT_ACTION_MOVE;
- }
- }
-
+ // TODO(b/327503168): Move this check inside MotionEvent::split once all callers handle it
+ // correctly.
if (action == AMOTION_EVENT_ACTION_DOWN && splitDownTime != originalMotionEntry.eventTime) {
logDispatchStateLocked();
LOG_ALWAYS_FATAL("Split motion event has mismatching downTime and eventTime for "
@@ -4361,7 +4315,7 @@
originalMotionEntry.yPrecision,
originalMotionEntry.xCursorPosition,
originalMotionEntry.yCursorPosition, splitDownTime,
- splitPointerProperties, splitPointerCoords);
+ pointerProperties, pointerCoords);
return splitMotionEntry;
}
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index be09013..0be64e6 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -84,24 +84,24 @@
void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
const InputTarget& target) {
- auto& cookieState = getState(cookie);
- if (!cookieState) {
+ auto& eventState = getState(cookie);
+ if (eventState.isEventProcessingComplete) {
LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
}
// TODO(b/210460522): Determine if the event is sensitive based on the target.
}
void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
- auto& cookieState = getState(cookie);
- if (!cookieState) {
+ auto& eventState = getState(cookie);
+ if (eventState.isEventProcessingComplete) {
LOG(FATAL) << "Traced event was already logged. "
"eventProcessingComplete() was likely called more than once.";
}
std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
[&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- cookieState->event);
- cookieState.reset();
+ eventState.event);
+ eventState.isEventProcessingComplete = true;
}
void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
@@ -136,7 +136,7 @@
/*hmac=*/{}});
}
-std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
+InputTracer::EventState& InputTracer::getState(const EventTrackerInterface& cookie) {
return static_cast<const EventTrackerImpl&>(cookie).mState;
}
@@ -146,18 +146,17 @@
: mTracer(tracer), mState(event) {}
InputTracer::EventTrackerImpl::~EventTrackerImpl() {
- if (!mState) {
+ if (mState.isEventProcessingComplete) {
// This event has already been written to the trace as expected.
return;
}
- // We're still holding on to the state, which means it hasn't yet been written to the trace.
- // Write it to the trace now.
+ // The event processing was never marked as complete, so do it now.
// TODO(b/210460522): Determine why/where the event is being destroyed before
// eventProcessingComplete() is called.
std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
[&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
- mState->event);
- mState.reset();
+ mState.event);
+ mState.isEventProcessingComplete = true;
}
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index c8b25c9..1acac6d 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -51,13 +51,16 @@
// The state of a tracked event.
struct EventState {
+ explicit inline EventState(TracedEvent event) : event(std::move(event)){};
+
const TracedEvent event;
+ bool isEventProcessingComplete{false};
// TODO(b/210460522): Add additional args for tracking event sensitivity and
// dispatch target UIDs.
};
// Get the event state associated with a tracking cookie.
- std::optional<EventState>& getState(const EventTrackerInterface&);
+ EventState& getState(const EventTrackerInterface&);
// Implementation of the event tracker cookie. The cookie holds the event state directly for
// convenience to avoid the overhead of tracking the state separately in InputTracer.
@@ -68,11 +71,9 @@
private:
InputTracer& mTracer;
- // This event tracker cookie will only hold the state as long as it has not been written
- // to the trace. The state is released when the event is written to the trace.
- mutable std::optional<EventState> mState;
+ mutable EventState mState;
- friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ friend EventState& InputTracer::getState(const EventTrackerInterface&);
};
};
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 8b47b55..462aedc 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -25,6 +25,9 @@
*
* This is the interface that PointerChoreographer uses to talk to Window Manager and other
* system components.
+ *
+ * NOTE: In general, the PointerChoreographer must not interact with the policy while
+ * holding any locks.
*/
class PointerChoreographerPolicyInterface {
public:
@@ -37,6 +40,9 @@
* for and runnable on the host, the PointerController implementation must be in a separate
* library, libinputservice, that has the additional dependencies. The PointerController
* will be mocked when testing PointerChoreographer.
+ *
+ * Since this is a factory method used to work around dependencies, it will not interact with
+ * other input components and may be called with the PointerChoreographer lock held.
*/
virtual std::shared_ptr<PointerControllerInterface> createPointerController(
PointerControllerInterface::ControllerType type) = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 45f09ae..06f10e5 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -40,6 +40,8 @@
// The default velocity control parameters that has no effect.
static const VelocityControlParameters FLAT_VELOCITY_CONTROL_PARAMS{};
+static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+
// --- CursorMotionAccumulator ---
CursorMotionAccumulator::CursorMotionAccumulator() {
@@ -76,9 +78,14 @@
CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
+ : CursorInputMapper(deviceContext, readerConfig, ENABLE_POINTER_CHOREOGRAPHER) {}
+
+CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ bool enablePointerChoreographer)
: InputMapper(deviceContext, readerConfig),
mLastEventTime(std::numeric_limits<nsecs_t>::min()),
- mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()),
+ mEnablePointerChoreographer(enablePointerChoreographer),
mEnableNewMousePointerBallistics(input_flags::enable_new_mouse_pointer_ballistics()) {}
CursorInputMapper::~CursorInputMapper() {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 1ddf6f2..ca541d9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -133,6 +133,10 @@
explicit CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
+ // Constructor for testing.
+ explicit CursorInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ bool enablePointerChoreographer);
void dumpParameters(std::string& dump);
void configureBasicParams();
void configureOnPointerCapture(const InputReaderConfiguration& config);
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index b990dd5..eacc66e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -47,6 +47,8 @@
namespace {
+static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+
/**
* Log details of each gesture output by the gestures library.
* Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
@@ -232,6 +234,11 @@
TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
+ : TouchpadInputMapper(deviceContext, readerConfig, ENABLE_POINTER_CHOREOGRAPHER) {}
+
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ bool enablePointerChoreographer)
: InputMapper(deviceContext, readerConfig),
mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
mPointerController(getContext()->getPointerController(getDeviceId())),
@@ -240,7 +247,7 @@
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())),
- mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
+ mEnablePointerChoreographer(enablePointerChoreographer) {
RawAbsoluteAxisInfo slotAxisInfo;
deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 897edca..9f272cf 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -72,6 +72,10 @@
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
+ // Constructor for testing.
+ explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ bool enablePointerChoreographer);
void updatePalmDetectionMetrics();
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 8c17221..de74067 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -157,9 +157,12 @@
mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
}
+ virtual bool isPointerChoreographerEnabled() { return false; }
+
void createMapper() {
createDevice();
- mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration,
+ isPointerChoreographerEnabled());
}
void setPointerCapture(bool enabled) {
@@ -194,10 +197,11 @@
class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
protected:
void SetUp() override {
- input_flags::enable_pointer_choreographer(false);
input_flags::enable_new_mouse_pointer_ballistics(false);
CursorInputMapperUnitTestBase::SetUp();
}
+
+ bool isPointerChoreographerEnabled() override { return false; }
};
TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) {
@@ -321,10 +325,10 @@
// Disable pointer capture. Afterwards, events should be generated the usual way.
setPointerCapture(false);
- const auto expectedCoords = input_flags::enable_pointer_choreographer()
+ const auto expectedCoords = CursorInputMapperUnitTest::isPointerChoreographerEnabled()
? WithCoords(0, 0)
: WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
- const auto expectedCursorPosition = input_flags::enable_pointer_choreographer()
+ const auto expectedCursorPosition = CursorInputMapperUnitTest::isPointerChoreographerEnabled()
? WithCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION)
: WithCursorPosition(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
args.clear();
@@ -708,7 +712,9 @@
createDevice();
// Associate the InputDevice with the secondary display.
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ mMapper = createInputMapper<
+ CursorInputMapper>(deviceContext, mReaderConfiguration,
+ CursorInputMapperUnitTest::isPointerChoreographerEnabled());
// Ensure input events are generated for the secondary display.
std::list<NotifyArgs> args;
@@ -731,7 +737,9 @@
createDevice();
// Associate the InputDevice with the secondary display.
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ mMapper = createInputMapper<
+ CursorInputMapper>(deviceContext, mReaderConfiguration,
+ CursorInputMapperUnitTest::isPointerChoreographerEnabled());
// The mapper should not generate any events because it is associated with a display that is
// different from the pointer display.
@@ -837,7 +845,9 @@
: public CursorInputMapperUnitTest,
public testing::WithParamInterface<
std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
- int32_t /*expectedKeyCode*/>> {};
+ int32_t /*expectedKeyCode*/>> {
+ virtual bool isPointerChoreographerEnabled() override { return false; }
+};
TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKey) {
auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
@@ -956,10 +966,11 @@
class CursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
protected:
void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
input_flags::enable_new_mouse_pointer_ballistics(false);
CursorInputMapperUnitTestBase::SetUp();
}
+
+ bool isPointerChoreographerEnabled() override { return true; }
};
TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) {
@@ -1288,10 +1299,11 @@
class CursorInputMapperUnitTestWithNewBallistics : public CursorInputMapperUnitTestBase {
protected:
void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
input_flags::enable_new_mouse_pointer_ballistics(true);
CursorInputMapperUnitTestBase::SetUp();
}
+
+ bool isPointerChoreographerEnabled() override { return true; }
};
TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) {
@@ -1413,7 +1425,6 @@
class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
protected:
void SetUp() override {
- input_flags::enable_pointer_choreographer(false);
SetUpWithBus(BUS_BLUETOOTH);
mFakePointerController = std::make_shared<FakePointerController>();
@@ -1531,12 +1542,13 @@
class BluetoothCursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
protected:
void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
SetUpWithBus(BUS_BLUETOOTH);
mFakePointerController = std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mFakePointerController);
}
+
+ bool isPointerChoreographerEnabled() override { return true; }
};
TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmoothening) {
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index fbafbad..a92dce5 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -117,8 +117,11 @@
return base::ResultError("Axis not supported", NAME_NOT_FOUND);
});
createDevice();
- mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
+ mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration,
+ isPointerChoreographerEnabled());
}
+
+ virtual bool isPointerChoreographerEnabled() { return false; }
};
class TouchpadInputMapperTest : public TouchpadInputMapperTestBase {
@@ -182,10 +185,9 @@
class TouchpadInputMapperTestWithChoreographer : public TouchpadInputMapperTestBase {
protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
- TouchpadInputMapperTestBase::SetUp();
- }
+ void SetUp() override { TouchpadInputMapperTestBase::SetUp(); }
+
+ bool isPointerChoreographerEnabled() override { return true; }
};
// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 1f72e8b..3ea08fe 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -17,6 +17,7 @@
"PowerHalController.cpp",
"PowerHalLoader.cpp",
"PowerHalWrapper.cpp",
+ "PowerHintSessionWrapper.cpp",
"PowerSaveState.cpp",
"Temperature.cpp",
"WorkDuration.cpp",
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index bc178bc..40fd097 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -57,6 +57,10 @@
PowerHalLoader::unloadAll();
}
+int32_t HalConnector::getAidlVersion() {
+ return PowerHalLoader::getAidlVersion();
+}
+
// -------------------------------------------------------------------------------------------------
void PowerHalController::init() {
@@ -77,6 +81,22 @@
return mConnectedHal;
}
+// Using statement expression macro instead of a method lets the static be
+// scoped to the outer method while dodging the need for a support lookup table
+// This only works for AIDL methods that do not vary supported/unsupported depending
+// on their arguments (not setBoost, setMode) which do their own support checks
+#define CACHE_SUPPORT(version, method) \
+ ({ \
+ static bool support = mHalConnector->getAidlVersion() >= version; \
+ !support ? decltype(method)::unsupported() : ({ \
+ auto result = method; \
+ if (result.isUnsupported()) { \
+ support = false; \
+ } \
+ std::move(result); \
+ }); \
+ })
+
// Check if a call to Power HAL function failed; if so, log the failure and
// invalidate the current Power HAL handle.
template <typename T>
@@ -103,40 +123,49 @@
return processHalResult(handle->setMode(mode, enabled), "setMode");
}
-HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
-PowerHalController::createHintSession(int32_t tgid, int32_t uid,
- const std::vector<int32_t>& threadIds,
- int64_t durationNanos) {
+// Aidl-only methods
+
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> PowerHalController::createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->createHintSession(tgid, uid, threadIds, durationNanos),
- "createHintSession");
+ return CACHE_SUPPORT(2,
+ processHalResult(handle->createHintSession(tgid, uid, threadIds,
+ durationNanos),
+ "createHintSession"));
}
-HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
-PowerHalController::createHintSessionWithConfig(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> PowerHalController::createHintSessionWithConfig(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
aidl::android::hardware::power::SessionTag tag,
aidl::android::hardware::power::SessionConfig* config) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
- tag, config),
- "createHintSessionWithConfig");
+ return CACHE_SUPPORT(5,
+ processHalResult(handle->createHintSessionWithConfig(tgid, uid, threadIds,
+ durationNanos, tag,
+ config),
+ "createHintSessionWithConfig"));
}
HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->getHintSessionPreferredRate(), "getHintSessionPreferredRate");
+ return CACHE_SUPPORT(2,
+ processHalResult(handle->getHintSessionPreferredRate(),
+ "getHintSessionPreferredRate"));
}
HalResult<aidl::android::hardware::power::ChannelConfig> PowerHalController::getSessionChannel(
int tgid, int uid) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->getSessionChannel(tgid, uid), "getSessionChannel");
+ return CACHE_SUPPORT(5,
+ processHalResult(handle->getSessionChannel(tgid, uid),
+ "getSessionChannel"));
}
HalResult<void> PowerHalController::closeSessionChannel(int tgid, int uid) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->closeSessionChannel(tgid, uid), "closeSessionChannel");
+ return CACHE_SUPPORT(5,
+ processHalResult(handle->closeSessionChannel(tgid, uid),
+ "closeSessionChannel"));
}
} // namespace power
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 2214461..ea284c3 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -60,6 +60,7 @@
sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
sp<V1_3::IPower> PowerHalLoader::gHalHidlV1_3 = nullptr;
+int32_t PowerHalLoader::gAidlInterfaceVersion = 0;
void PowerHalLoader::unloadAll() {
std::lock_guard<std::mutex> lock(gHalMutex);
@@ -89,6 +90,8 @@
ndk::SpAIBinder(AServiceManager_waitForService(aidlServiceName.c_str())));
if (gHalAidl) {
ALOGI("Successfully connected to Power HAL AIDL service.");
+ gHalAidl->getInterfaceVersion(&gAidlInterfaceVersion);
+
} else {
ALOGI("Power HAL AIDL service not available.");
gHalExists = false;
@@ -128,6 +131,10 @@
return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
}
+int32_t PowerHalLoader::getAidlVersion() {
+ return gAidlInterfaceVersion;
+}
+
// -------------------------------------------------------------------------------------------------
} // namespace power
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 1009100..bd6685c 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -18,11 +18,10 @@
#include <aidl/android/hardware/power/Boost.h>
#include <aidl/android/hardware/power/IPowerHintSession.h>
#include <aidl/android/hardware/power/Mode.h>
+#include <powermanager/HalResult.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-#include <cinttypes>
-
using namespace android::hardware::power;
namespace Aidl = aidl::android::hardware::power;
@@ -31,15 +30,6 @@
namespace power {
// -------------------------------------------------------------------------------------------------
-inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) {
- if (result.isOk()) {
- return HalResult<void>::ok();
- }
- ALOGE("Power HAL request failed: %s", result.getDescription().c_str());
- return HalResult<void>::failed(result.getDescription());
-}
-
-// -------------------------------------------------------------------------------------------------
HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
ALOGV("Skipped setBoost %s with duration %dms because %s", toString(boost).c_str(), durationMs,
@@ -53,19 +43,19 @@
return HalResult<void>::unsupported();
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> EmptyHalWrapper::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because %s", threadIds.size(),
getUnsupportedMessage());
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::unsupported();
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSessionWithConfig(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> EmptyHalWrapper::createHintSessionWithConfig(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t, Aidl::SessionTag,
Aidl::SessionConfig*) {
ALOGV("Skipped createHintSessionWithConfig(task num=%zu) because %s", threadIds.size(),
getUnsupportedMessage());
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::unsupported();
}
HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
@@ -225,7 +215,7 @@
}
lock.unlock();
- return toHalResult(mHandle->setBoost(boost, durationMs));
+ return HalResult<void>::fromStatus(mHandle->setBoost(boost, durationMs));
}
HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
@@ -253,25 +243,25 @@
}
lock.unlock();
- return toHalResult(mHandle->setMode(mode, enabled));
+ return HalResult<void>::fromStatus(mHandle->setMode(mode, enabled));
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> AidlHalWrapper::createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
std::shared_ptr<Aidl::IPowerHintSession> appSession;
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
- std::move(appSession));
+ std::make_shared<PowerHintSessionWrapper>(std::move(appSession)));
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSessionWithConfig(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> AidlHalWrapper::createHintSessionWithConfig(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
Aidl::SessionTag tag, Aidl::SessionConfig* config) {
std::shared_ptr<Aidl::IPowerHintSession> appSession;
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
fromStatus(mHandle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
tag, config, &appSession),
- std::move(appSession));
+ std::make_shared<PowerHintSessionWrapper>(std::move(appSession)));
}
HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() {
@@ -287,7 +277,7 @@
}
HalResult<void> AidlHalWrapper::closeSessionChannel(int tgid, int uid) {
- return toHalResult(mHandle->closeSessionChannel(tgid, uid));
+ return HalResult<void>::fromStatus(mHandle->closeSessionChannel(tgid, uid));
}
const char* AidlHalWrapper::getUnsupportedMessage() {
diff --git a/services/powermanager/PowerHintSessionWrapper.cpp b/services/powermanager/PowerHintSessionWrapper.cpp
new file mode 100644
index 0000000..930c7fa
--- /dev/null
+++ b/services/powermanager/PowerHintSessionWrapper.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 <powermanager/PowerHintSessionWrapper.h>
+
+using namespace aidl::android::hardware::power;
+
+namespace android::power {
+
+// Caches support for a given call in a static variable, checking both
+// the return value and interface version.
+#define CACHE_SUPPORT(version, method) \
+ ({ \
+ static bool support = mInterfaceVersion >= version; \
+ !support ? decltype(method)::unsupported() : ({ \
+ auto result = method; \
+ if (result.isUnsupported()) { \
+ support = false; \
+ } \
+ std::move(result); \
+ }); \
+ })
+
+#define CHECK_SESSION(resultType) \
+ if (mSession == nullptr) { \
+ return HalResult<resultType>::failed("Session not running"); \
+ }
+
+// FWD_CALL just forwards calls from the wrapper to the session object.
+// It only works if the call has no return object, as is the case with all calls
+// except getSessionConfig.
+#define FWD_CALL(version, name, args, untypedArgs) \
+ HalResult<void> PowerHintSessionWrapper::name args { \
+ CHECK_SESSION(void) \
+ return CACHE_SUPPORT(version, HalResult<void>::fromStatus(mSession->name untypedArgs)); \
+ }
+
+PowerHintSessionWrapper::PowerHintSessionWrapper(std::shared_ptr<IPowerHintSession>&& session)
+ : mSession(session) {
+ if (mSession != nullptr) {
+ mSession->getInterfaceVersion(&mInterfaceVersion);
+ }
+}
+
+// Support for individual hints/modes is not really handled here since there
+// is no way to check for it, so in the future if a way to check that is added,
+// this will need to be updated.
+
+FWD_CALL(2, updateTargetWorkDuration, (int64_t in_targetDurationNanos), (in_targetDurationNanos));
+FWD_CALL(2, reportActualWorkDuration, (const std::vector<WorkDuration>& in_durations),
+ (in_durations));
+FWD_CALL(2, pause, (), ());
+FWD_CALL(2, resume, (), ());
+FWD_CALL(2, close, (), ());
+FWD_CALL(4, sendHint, (SessionHint in_hint), (in_hint));
+FWD_CALL(4, setThreads, (const std::vector<int32_t>& in_threadIds), (in_threadIds));
+FWD_CALL(5, setMode, (SessionMode in_type, bool in_enabled), (in_type, in_enabled));
+
+HalResult<SessionConfig> PowerHintSessionWrapper::getSessionConfig() {
+ CHECK_SESSION(SessionConfig);
+ SessionConfig config;
+ return CACHE_SUPPORT(5,
+ HalResult<SessionConfig>::fromStatus(mSession->getSessionConfig(&config),
+ std::move(config)));
+}
+
+} // namespace android::power
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 6fc96c0..a05ce2b 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -37,6 +37,7 @@
"PowerHalWrapperHidlV1_1Test.cpp",
"PowerHalWrapperHidlV1_2Test.cpp",
"PowerHalWrapperHidlV1_3Test.cpp",
+ "PowerHintSessionWrapperTest.cpp",
"WorkSourceTest.cpp",
],
cflags: [
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index a720296..1589c99 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -86,6 +86,10 @@
void PowerHalWrapperAidlTest::SetUp() {
mMockHal = ndk::SharedRefBase::make<StrictMock<MockIPower>>();
+ EXPECT_CALL(*mMockHal, getInterfaceVersion(_)).WillRepeatedly(([](int32_t* ret) {
+ *ret = 5;
+ return ndk::ScopedAStatus::ok();
+ }));
mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
ASSERT_NE(nullptr, mWrapper);
}
@@ -130,10 +134,12 @@
}
TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
- EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
- .Times(Exactly(1))
- .WillOnce(DoAll(SetArgPointee<1>(false),
- Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(_, _))
+ .Times(Exactly(2))
+ .WillRepeatedly([](Boost, bool* ret) {
+ *ret = false;
+ return ndk::ScopedAStatus::ok();
+ });
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
ASSERT_TRUE(result.isUnsupported());
@@ -311,3 +317,29 @@
auto closeResult = mWrapper->closeSessionChannel(tgid, uid);
ASSERT_TRUE(closeResult.isOk());
}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigUnsupported) {
+ std::vector<int> threadIds{gettid()};
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ int64_t durationNanos = 16666666L;
+ SessionTag tag = SessionTag::OTHER;
+ SessionConfig out;
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSessionWithConfig(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos),
+ Eq(tag), _, _))
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(
+ ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))));
+ auto result =
+ mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
+ ASSERT_TRUE(result.isUnsupported());
+ Mock::VerifyAndClearExpectations(mMockHal.get());
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSessionWithConfig(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos),
+ Eq(tag), _, _))
+ .WillOnce(Return(
+ testing::ByMove(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))));
+ result = mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/powermanager/tests/PowerHintSessionWrapperTest.cpp b/services/powermanager/tests/PowerHintSessionWrapperTest.cpp
new file mode 100644
index 0000000..7743fa4
--- /dev/null
+++ b/services/powermanager/tests/PowerHintSessionWrapperTest.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 <aidl/android/hardware/power/IPowerHintSession.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using aidl::android::hardware::power::IPowerHintSession;
+using android::power::PowerHintSessionWrapper;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+class MockIPowerHintSession : public IPowerHintSession {
+public:
+ MockIPowerHintSession() = default;
+ MOCK_METHOD(::ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t in_targetDurationNanos),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, reportActualWorkDuration,
+ (const std::vector<::aidl::android::hardware::power::WorkDuration>& in_durations),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, pause, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, resume, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, close, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, sendHint,
+ (::aidl::android::hardware::power::SessionHint in_hint), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setThreads, (const std::vector<int32_t>& in_threadIds),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setMode,
+ (::aidl::android::hardware::power::SessionMode in_type, bool in_enabled),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getSessionConfig,
+ (::aidl::android::hardware::power::SessionConfig * _aidl_return), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override));
+ MOCK_METHOD(::ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+};
+
+class PowerHintSessionWrapperTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::shared_ptr<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
+ std::unique_ptr<PowerHintSessionWrapper> mSession = nullptr;
+};
+
+void PowerHintSessionWrapperTest::SetUp() {
+ mMockSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
+ EXPECT_CALL(*mMockSession, getInterfaceVersion(_)).WillRepeatedly(([](int32_t* ret) {
+ *ret = 5;
+ return ndk::ScopedAStatus::ok();
+ }));
+ mSession = std::make_unique<PowerHintSessionWrapper>(mMockSession);
+ ASSERT_NE(nullptr, mSession);
+}
+
+TEST_F(PowerHintSessionWrapperTest, updateTargetWorkDuration) {
+ EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1000000000))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->updateTargetWorkDuration(1000000000);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, reportActualWorkDuration) {
+ EXPECT_CALL(*mMockSession.get(),
+ reportActualWorkDuration(
+ std::vector<::aidl::android::hardware::power::WorkDuration>()))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->reportActualWorkDuration(
+ std::vector<::aidl::android::hardware::power::WorkDuration>());
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, pause) {
+ EXPECT_CALL(*mMockSession.get(), pause()).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->pause();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, resume) {
+ EXPECT_CALL(*mMockSession.get(), resume()).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->resume();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, close) {
+ EXPECT_CALL(*mMockSession.get(), close()).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->close();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, sendHint) {
+ EXPECT_CALL(*mMockSession.get(),
+ sendHint(::aidl::android::hardware::power::SessionHint::CPU_LOAD_UP))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->sendHint(::aidl::android::hardware::power::SessionHint::CPU_LOAD_UP);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, setThreads) {
+ EXPECT_CALL(*mMockSession.get(), setThreads(_)).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->setThreads(std::vector<int32_t>{gettid()});
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, setMode) {
+ EXPECT_CALL(*mMockSession.get(),
+ setMode(::aidl::android::hardware::power::SessionMode::POWER_EFFICIENCY, true))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->setMode(::aidl::android::hardware::power::SessionMode::POWER_EFFICIENCY,
+ true);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, getSessionConfig) {
+ EXPECT_CALL(*mMockSession.get(), getSessionConfig(_))
+ .WillOnce(DoAll(SetArgPointee<0>(
+ aidl::android::hardware::power::SessionConfig{.id = 12L}),
+ Return(ndk::ScopedAStatus::ok())));
+ auto status = mSession->getSessionConfig();
+ ASSERT_TRUE(status.isOk());
+}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 118d928..bd54d24 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -340,8 +340,8 @@
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
- // This callback is used for additional automotive-specific states for sensor privacy
- // such as AUTO_DRIVER_ASSISTANCE_APPS. The newly defined states will only be valid
+ // This callback is used for additional automotive-specific state for sensor privacy
+ // such as ENABLED_EXCEPT_ALLOWLISTED_APPS. The newly defined states will only be valid
// for camera privacy on automotive devices. onSensorPrivacyChanged() will still be
// invoked whenever the enabled status of a toggle changes.
binder::Status onSensorPrivacyStateChanged(int, int, int) {return binder::Status::ok();}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 6a0ea22..d771803 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -12,7 +12,10 @@
name: "surfaceflinger_flags",
package: "com.android.graphics.surfaceflinger.flags",
container: "system",
- srcs: ["surfaceflinger_flags.aconfig"],
+ srcs: [
+ "surfaceflinger_flags.aconfig",
+ "surfaceflinger_flags_new.aconfig",
+ ],
}
cc_aconfig_library {
@@ -42,6 +45,7 @@
"android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
+ "libsurfaceflinger_common_deps",
"surfaceflinger_defaults",
],
cflags: [
@@ -82,7 +86,6 @@
"libinput",
"libutils",
"libSurfaceFlingerProp",
- "server_configurable_flags",
],
static_libs: [
"libaidlcommonsupport",
@@ -95,10 +98,8 @@
"libscheduler",
"libserviceutils",
"libshaders",
- "libsurfaceflinger_common",
"libtimestats",
"libtonemap",
- "libsurfaceflingerflags",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 0b01c66..a52cc87 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -38,7 +38,6 @@
"libSurfaceFlingerProp",
"libui",
"libutils",
- "server_configurable_flags",
],
static_libs: [
"liblayers_proto",
@@ -90,24 +89,23 @@
cc_library {
name: "libcompositionengine",
- defaults: ["libcompositionengine_defaults"],
- static_libs: [
- "libsurfaceflinger_common",
- "libsurfaceflingerflags",
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_deps",
],
srcs: [
":libcompositionengine_sources",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
- shared_libs: [
- "server_configurable_flags",
- ],
}
cc_library {
name: "libcompositionengine_mocks",
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
"mock/CompositionEngine.cpp",
"mock/Display.cpp",
@@ -123,11 +121,6 @@
"libgtest",
"libgmock",
"libcompositionengine",
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
- ],
- shared_libs: [
- "server_configurable_flags",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -140,7 +133,10 @@
"frameworks/native/services/surfaceflinger/common/include",
"frameworks/native/services/surfaceflinger/tests/unittests",
],
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
":libcompositionengine_sources",
"tests/planner/CachedSetTest.cpp",
@@ -166,14 +162,11 @@
"librenderengine_mocks",
"libgmock",
"libgtest",
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
],
shared_libs: [
// For some reason, libvulkan isn't picked up from librenderengine
// Probably ASAN related?
"libvulkan",
- "server_configurable_flags",
],
sanitize: {
hwaddress: true,
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index a0c943b..8dfbeb8 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -233,7 +233,7 @@
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
- ret.getDescription().c_str());
+ ret.errorMessage());
mHintSession = nullptr;
}
}
@@ -293,8 +293,7 @@
auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
if (!ret.isOk()) {
- ALOGW("Failed to report actual work durations with error: %s",
- ret.getDescription().c_str());
+ ALOGW("Failed to report actual work durations with error: %s", ret.errorMessage());
mHintSession = nullptr;
return;
}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index bbe51cc0..1040048 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -259,8 +259,8 @@
std::optional<bool> mSupportsHintSession;
std::mutex mHintSessionMutex;
- std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
- GUARDED_BY(mHintSessionMutex) = nullptr;
+ std::shared_ptr<power::PowerHintSessionWrapper> mHintSession GUARDED_BY(mHintSessionMutex) =
+ nullptr;
// Initialize to true so we try to call, to check if it's supported
bool mHasExpensiveRendering = true;
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 16776cf..5455fdc 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -53,7 +53,10 @@
cc_test {
name: "libscheduler_test",
test_suites: ["device-tests"],
- defaults: ["libscheduler_defaults"],
+ defaults: [
+ "libscheduler_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
"tests/FrameTargeterTest.cpp",
"tests/PresentLatencyTrackerTest.cpp",
@@ -63,9 +66,5 @@
"libgmock",
"libgtest",
"libscheduler",
- "libsurfaceflingerflags_test",
- ],
- shared_libs: [
- "server_configurable_flags",
],
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index dcb6254..b8d5e76 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -300,12 +300,26 @@
if (gameModeFrameRateOverride.isValid()) {
info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
+ ATRACE_FORMAT_INSTANT("GameModeFrameRateOverride");
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, gameFrameRateOverrideVoteType,
+ gameModeFrameRateOverride.getIntValue());
+ }
} else if (frameRate.isValid()) {
info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate,
frameRate.vote.seamlessness, frameRate.category});
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, gameFrameRateOverrideVoteType,
+ frameRate.vote.rate.getIntValue());
+ }
} else if (gameDefaultFrameRateOverride.isValid()) {
info->setLayerVote(
{gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride});
+ ATRACE_FORMAT_INSTANT("GameDefaultFrameRateOverride");
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, gameFrameRateOverrideVoteType,
+ gameDefaultFrameRateOverride.getIntValue());
+ }
} else {
info->resetLayerVote();
}
@@ -341,9 +355,18 @@
std::string LayerHistory::dump() const {
std::lock_guard lock(mLock);
- return base::StringPrintf("{size=%zu, active=%zu}",
+ return base::StringPrintf("{size=%zu, active=%zu}\n\tGameFrameRateOverrides=\n\t\t%s",
mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
- mActiveLayerInfos.size());
+ mActiveLayerInfos.size(), dumpGameFrameRateOverridesLocked().c_str());
+}
+
+std::string LayerHistory::dumpGameFrameRateOverridesLocked() const {
+ std::string overridesString = "(uid, gameModeOverride, gameDefaultOverride)=";
+ for (auto it = mGameFrameRateOverride.begin(); it != mGameFrameRateOverride.end(); ++it) {
+ base::StringAppendF(&overridesString, "{%u, %d %d} ", it->first,
+ it->second.first.getIntValue(), it->second.second.getIntValue());
+ }
+ return overridesString;
}
float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 930d06c..a6f1b56 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -108,6 +108,8 @@
// keyed by id as returned from Layer::getSequence()
using LayerInfos = std::unordered_map<int32_t, LayerPair>;
+ std::string dumpGameFrameRateOverridesLocked() const REQUIRES(mLock);
+
// Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive
// layers to mInactiveLayerInfos.
// worst case time complexity is O(2 * inactive + active)
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 93350b5..ad59f1a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -834,13 +834,16 @@
const bool touchBoostForExplicitExact = [&] {
if (supportsAppFrameRateOverrideByContent()) {
// Enable touch boost if there are other layers besides exact
- return explicitExact + noVoteLayers != layers.size();
+ return explicitExact + noVoteLayers + explicitGteLayers != layers.size();
} else {
// Enable touch boost if there are no exact layers
return explicitExact == 0;
}
}();
+ const bool touchBoostForCategory =
+ explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
+
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
@@ -851,6 +854,7 @@
const bool hasInteraction = signals.touch || interactiveLayers > 0;
if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+ touchBoostForCategory &&
scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
@@ -948,17 +952,43 @@
const auto layersByUid = groupLayersByUid(layers);
UidToFrameRateOverride frameRateOverrides;
for (const auto& [uid, layersWithSameUid] : layersByUid) {
- // Layers with ExplicitExactOrMultiple expect touch boost
- const bool hasExplicitExactOrMultiple =
- std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(),
- [](const auto& layer) {
- return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
- });
+ // Look for cases that should not have frame rate overrides.
+ bool hasExplicitExactOrMultiple = false;
+ bool hasExplicitDefault = false;
+ bool hasHighHint = false;
+ for (const auto& layer : layersWithSameUid) {
+ switch (layer->vote) {
+ case LayerVoteType::ExplicitExactOrMultiple:
+ hasExplicitExactOrMultiple = true;
+ break;
+ case LayerVoteType::ExplicitDefault:
+ hasExplicitDefault = true;
+ break;
+ case LayerVoteType::ExplicitCategory:
+ if (layer->frameRateCategory == FrameRateCategory::HighHint) {
+ hasHighHint = true;
+ }
+ break;
+ default:
+ // No action
+ break;
+ }
+ if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) {
+ break;
+ }
+ }
+ // Layers with ExplicitExactOrMultiple expect touch boost
if (globalSignals.touch && hasExplicitExactOrMultiple) {
continue;
}
+ // Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and
+ // skip frame rate override.
+ if (hasHighHint && !hasExplicitDefault) {
+ continue;
+ }
+
for (auto& [_, score] : scoredFrameRates) {
score = 0;
}
@@ -1528,19 +1558,17 @@
case FrameRateCategory::High:
return FpsRange{90_Hz, 120_Hz};
case FrameRateCategory::Normal:
- return FpsRange{60_Hz, 90_Hz};
+ return FpsRange{60_Hz, 120_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 30_Hz};
+ return FpsRange{30_Hz, 120_Hz};
case FrameRateCategory::HighHint:
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
ftl::enum_string(category).c_str());
- return FpsRange{0_Hz, 0_Hz};
default:
LOG_ALWAYS_FATAL("Invalid frame rate category for range: %s",
ftl::enum_string(category).c_str());
- return FpsRange{0_Hz, 0_Hz};
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 85bdc1c..2501f4b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -854,6 +854,9 @@
mCompositionEngine->getHwComposer().setCallback(*this);
ClientCache::getInstance().setRenderEngine(&getRenderEngine());
+ mHasReliablePresentFences =
+ !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
+
enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
@@ -927,9 +930,7 @@
// Inform native graphics APIs whether the present timestamp is supported:
- const bool presentFenceReliable =
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
- mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
+ mStartPropertySetThread = getFactory().createStartPropertySetThread(mHasReliablePresentFences);
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
@@ -1003,9 +1004,7 @@
FrameEvent::RELEASE,
};
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
- if (!getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ if (mHasReliablePresentFences) {
outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
}
return NO_ERROR;
@@ -3031,10 +3030,9 @@
// but that should be okay since CompositorTiming has snapping logic.
const TimePoint compositeTime =
TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp());
- const Duration presentLatency =
- getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
- ? Duration::zero()
- : mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime);
+ const Duration presentLatency = mHasReliablePresentFences
+ ? mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime)
+ : Duration::zero();
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
@@ -4357,7 +4355,7 @@
features |= Feature::kTracePredictedVsync;
}
if (!base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false) &&
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ mHasReliablePresentFences) {
features |= Feature::kPresentFences;
}
if (display->refreshRateSelector().kernelIdleTimerController()) {
@@ -8091,9 +8089,6 @@
return;
}
- // Snapshots must be taken from the main thread.
- auto layers = mScheduler->schedule([=]() { return getLayerSnapshots(); }).get();
-
// Loop over all visible layers to see whether there's any protected layer. A protected layer is
// typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
// A protected layer has no implication on whether it's secure, which is explicitly set by
@@ -8101,6 +8096,8 @@
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
+ // Snapshots must be taken from the main thread.
+ auto layers = mScheduler->schedule([=]() { return getLayerSnapshots(); }).get();
hasProtectedLayer = layersHasProtectedLayer(layers);
}
const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1ce8606..005f2e6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1224,6 +1224,7 @@
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
bool mIsUserBuild = true;
+ bool mHasReliablePresentFences = false;
// Can only accessed from the main thread, these members
// don't need synchronization
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index f2ff00b..4a89dd0 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -35,6 +35,7 @@
],
static_libs: [
"libsurfaceflingerflags",
+ "android.os.flags-aconfig-cc",
],
}
@@ -45,5 +46,30 @@
],
static_libs: [
"libsurfaceflingerflags_test",
+ "android.os.flags-aconfig-cc-test",
+ ],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_deps",
+ shared_libs: [
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
+ "android.os.flags-aconfig-cc",
+ ],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_test_deps",
+ shared_libs: [
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "libsurfaceflinger_common_test",
+ "libsurfaceflingerflags_test",
+ "android.os.flags-aconfig-cc-test",
],
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b7f06a9..3b669c6 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -26,6 +26,7 @@
#include <server_configurable_flags/get_flags.h>
#include <cinttypes>
+#include <android_os.h>
#include <com_android_graphics_surfaceflinger_flags.h>
namespace android {
@@ -109,6 +110,7 @@
/// Trunk stable server flags ///
DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
+ DUMP_SERVER_FLAG(adpf_use_fmq_channel);
/// Trunk stable readonly flags ///
DUMP_READ_ONLY_FLAG(connected_display);
@@ -158,7 +160,7 @@
return getServerConfigurableFlag(serverFlagName); \
}
-#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted) \
+#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted, owner) \
bool FlagManager::name() const { \
if (checkForBootCompleted) { \
LOG_ALWAYS_FATAL_IF(!mBootCompleted, \
@@ -166,21 +168,24 @@
__func__); \
} \
static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
- static const bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride); \
if (mUnitTestMode) { \
/* \
* When testing, we don't want to rely on the cached `value` or the debugOverride. \
*/ \
- return flags::name(); \
+ return owner ::name(); \
} \
return value; \
}
#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true)
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, flags)
#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false)
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, flags)
+
+#define FLAG_MANAGER_SERVER_FLAG_IMPORTED(name, syspropOverride, owner) \
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, owner)
/// Legacy server flags ///
FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
@@ -216,4 +221,7 @@
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
+/// Trunk stable server flags from outside SurfaceFlinger ///
+FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 241c814..763963e 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -49,6 +49,7 @@
/// Trunk stable server flags ///
bool refresh_rate_overlay_on_external_display() const;
+ bool adpf_use_fmq_channel() const;
/// Trunk stable readonly flags ///
bool connected_display() const;
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index eda0674..5174fa7 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -1,3 +1,5 @@
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
package: "com.android.graphics.surfaceflinger.flags"
container: "system"
@@ -25,6 +27,8 @@
is_fixed_read_only: true
}
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "enable_layer_command_batching"
namespace: "core_graphics"
@@ -49,6 +53,8 @@
is_fixed_read_only: true
}
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "hotplug2"
namespace: "core_graphics"
@@ -73,6 +79,8 @@
is_fixed_read_only: true
}
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "refresh_rate_overlay_on_external_display"
namespace: "core_graphics"
@@ -98,6 +106,8 @@
# is_fixed_read_only: true
# }
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "cache_when_source_crop_layer_only_moved"
namespace: "core_graphics"
@@ -130,6 +140,8 @@
is_fixed_read_only: true
}
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "game_default_frame_rate"
namespace: "game"
@@ -162,6 +174,8 @@
is_fixed_read_only: true
}
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "renderable_buffer_usage"
namespace: "core_graphics"
@@ -184,6 +198,8 @@
}
}
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
+
flag {
name: "dont_skip_on_early_ro"
namespace: "core_graphics"
@@ -205,3 +221,5 @@
purpose: PURPOSE_BUGFIX
}
}
+
+# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
new file mode 100644
index 0000000..5451752
--- /dev/null
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -0,0 +1,13 @@
+# IMPORTANT - please keep alphabetize to reduce merge conflicts
+
+package: "com.android.graphics.surfaceflinger.flags"
+container: "system"
+
+flag {
+ name: "dont_skip_on_early_ro2"
+ namespace: "core_graphics"
+ description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
+ bug: "273702768"
+} # dont_skip_on_early_ro2
+
+# IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index dab0a3f..925fe0b 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -27,6 +27,7 @@
defaults: [
"android.hardware.graphics.common-ndk_shared",
"surfaceflinger_defaults",
+ "libsurfaceflinger_common_test_deps",
],
test_suites: ["device-tests"],
srcs: [
@@ -66,7 +67,6 @@
static_libs: [
"liblayers_proto",
"android.hardware.graphics.composer@2.1",
- "libsurfaceflingerflags",
],
shared_libs: [
"android.hardware.graphics.common@1.2",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f529f7c..0c13db3 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -29,7 +29,7 @@
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockHWC2.cpp",
"mock/DisplayHardware/MockIPower.cpp",
- "mock/DisplayHardware/MockIPowerHintSession.cpp",
+ "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
@@ -149,6 +149,7 @@
"android.hardware.graphics.composer3-ndk_static",
"android.hardware.power-ndk_static",
"librenderengine_deps",
+ "libsurfaceflinger_common_test_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
@@ -173,13 +174,11 @@
"librenderengine_mocks",
"libscheduler",
"libserviceutils",
- "libsurfaceflinger_common_test",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
"libtonemap",
"perfetto_trace_protos",
- "libsurfaceflingerflags_test",
],
shared_libs: [
"android.hardware.configstore-utils",
@@ -208,7 +207,6 @@
"libsync",
"libui",
"libutils",
- "server_configurable_flags",
],
header_libs: [
"android.hardware.graphics.composer3-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 9c66a97..1d44a3e 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -26,8 +26,8 @@
#include <chrono>
#include <future>
#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockIPowerHintSession.h"
#include "mock/DisplayHardware/MockPowerHalController.h"
+#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
using namespace android;
using namespace android::Hwc2::mock;
@@ -54,7 +54,7 @@
TestableSurfaceFlinger mFlinger;
std::unique_ptr<PowerAdvisor> mPowerAdvisor;
MockPowerHalController* mMockPowerHalController;
- std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
+ std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
};
bool PowerAdvisorTest::sessionExists() {
@@ -68,25 +68,29 @@
mMockPowerHalController =
reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get());
ON_CALL(*mMockPowerHalController, getHintSessionPreferredRate)
- .WillByDefault(Return(HalResult<int64_t>::fromStatus(binder::Status::ok(), 16000)));
+ .WillByDefault(Return(
+ ByMove(HalResult<int64_t>::fromStatus(ndk::ScopedAStatus::ok(), 16000))));
}
void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
- mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
+ mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
if (returnValidSession) {
ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(
- Return(HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+ .WillByDefault([&](int32_t, int32_t, const std::vector<int32_t>&, int64_t) {
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+ fromStatus(ndk::ScopedAStatus::ok(), mMockPowerHintSession);
+ });
} else {
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(binder::Status::ok(), nullptr)));
+ ON_CALL(*mMockPowerHalController, createHintSession).WillByDefault([] {
+ return HalResult<
+ std::shared_ptr<PowerHintSessionWrapper>>::fromStatus(ndk::ScopedAStatus::ok(),
+ nullptr);
+ });
}
mPowerAdvisor->enablePowerHintSession(true);
mPowerAdvisor->startPowerHintSession({1, 2, 3});
ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
- .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillByDefault(Return(testing::ByMove(HalResult<void>::ok())));
}
void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
@@ -148,7 +152,7 @@
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
.Times(1)
- .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillOnce(Return(testing::ByMove(HalResult<void>::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
mPowerAdvisor->setDisplays(displayIds);
@@ -188,7 +192,7 @@
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
.Times(1)
- .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillOnce(Return(testing::ByMove(HalResult<void>::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -231,7 +235,7 @@
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
.Times(1)
- .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillOnce(Return(testing::ByMove(HalResult<void>::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -328,17 +332,17 @@
ON_CALL(*mMockPowerHintSession, sendHint).WillByDefault([&letSendHintFinish] {
letSendHintFinish.get_future().wait();
- return ndk::ScopedAStatus::fromExceptionCode(-127);
+ return HalResult<void>::fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127));
});
ON_CALL(*mMockPowerHintSession, reportActualWorkDuration).WillByDefault([] {
- return ndk::ScopedAStatus::fromExceptionCode(-127);
+ return HalResult<void>::fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127));
});
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(Return(
- HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr)));
+ ON_CALL(*mMockPowerHalController, createHintSession).WillByDefault([] {
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+ fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr);
+ });
// First background call, to notice the session is down
auto firstHint = std::async(std::launch::async, [this] {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 39a8aac..fe0e3d1 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -259,6 +259,44 @@
config.enableFrameRateOverride = GetParam();
return TestableRefreshRateSelector(modes, activeModeId, config);
}
+
+ template <class T>
+ void testFrameRateCategoryWithMultipleLayers(const std::initializer_list<T>& testCases,
+ const TestableRefreshRateSelector& selector) {
+ std::vector<LayerRequirement> layers;
+ for (auto testCase : testCases) {
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate)
+ << ftl::enum_string(testCase.frameRateCategory) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate,
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps())
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ }
+ }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1542,6 +1580,96 @@
}
}
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withFrameRateCategoryMultiLayers_30_60_90_120) {
+ auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {0_Hz, FrameRateCategory::Normal, 60_Hz},
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {30_Hz, FrameRateCategory::High, 90_Hz},
+ {24_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal, 120_Hz},
+ {30_Hz, FrameRateCategory::NoPreference, 120_Hz},
+
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {24_Hz, FrameRateCategory::Default, 120_Hz},
+ {30_Hz, FrameRateCategory::Default, 120_Hz},
+ {120_Hz, FrameRateCategory::Default, 120_Hz},
+ },
+ selector);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategoryMultiLayers_60_120) {
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ testFrameRateCategoryWithMultipleLayers(std::initializer_list<
+ Case>{{0_Hz, FrameRateCategory::High, 120_Hz},
+ {0_Hz, FrameRateCategory::NoPreference,
+ 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 120_Hz},
+ {0_Hz, FrameRateCategory::NoPreference,
+ 120_Hz}},
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(std::initializer_list<
+ Case>{{24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal,
+ 120_Hz},
+ {30_Hz, FrameRateCategory::NoPreference,
+ 120_Hz}},
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {24_Hz, FrameRateCategory::Default, 120_Hz},
+ {30_Hz, FrameRateCategory::Default, 120_Hz},
+ {120_Hz, FrameRateCategory::Default, 120_Hz},
+ },
+ selector);
+}
+
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
@@ -1665,6 +1793,7 @@
lr1.frameRateCategory = FrameRateCategory::HighHint;
lr1.name = "ExplicitCategory HighHint";
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.frameRateCategory = FrameRateCategory::Default;
lr2.desiredRefreshRate = 30_Hz;
lr2.name = "30Hz ExplicitExactOrMultiple";
actualRankedFrameRates = selector.getRankedFrameRates(layers);
@@ -1691,6 +1820,153 @@
EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
}
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz Heuristic";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Min;
+ lr2.name = "Min";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_TouchBoost) {
+ auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ // No touch boost, for example a game that uses setFrameRate(30, default compatibility).
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::HighHint;
+ lr2.name = "ExplicitCategory HighHint";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::Low;
+ lr2.name = "ExplicitCategory Low";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.frameRateCategory = FrameRateCategory::Default;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitExact;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExact";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ if (selector.supportsAppFrameRateOverrideByContent()) {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+ }
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Min;
+ lr2.name = "Min";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "30Hz Heuristic";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitGte;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitGte";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
}
TEST_P(RefreshRateSelectorTest,
@@ -1725,8 +2001,8 @@
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 120_Hz, kModeId120},
- {FrameRateCategory::Normal, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Normal, true, 120_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
@@ -3159,6 +3435,210 @@
EXPECT_TRUE(frameRateOverrides.empty());
}
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+ {.ownerUid = 1234, .weight = 1.f}};
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitCategory High";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::High;
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitCategory Normal";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Normal;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitCategory Low";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Low;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitCategory NoPreference";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::NoPreference;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // HighHint case *without* touch boost has frame rate override.
+ // For example, game and touch interaction.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitDefault 60";
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].desiredRefreshRate = 60_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Default;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitExactOrMultiple 30";
+ layers[1].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[1].desiredRefreshRate = 30_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Default;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitExact 60";
+ layers[1].vote = LayerVoteType::ExplicitExact;
+ layers[1].desiredRefreshRate = 60_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Default;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // HighHint case with touch boost and thus should skip frame rate override.
+ layers[0].name = "ExplicitCategory HighHint";
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].desiredRefreshRate = 0_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[1].name = "ExplicitGte 60";
+ layers[1].vote = LayerVoteType::ExplicitGte;
+ layers[1].desiredRefreshRate = 60_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Default;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+
+ // ExplicitCategory case that expects no global touch boost and thus has frame rate override.
+ layers[0].name = "ExplicitDefault 60";
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::Default;
+ layers[1].name = "ExplicitCategory High";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::High;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(120_Hz, frameRateOverrides.at(1234));
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(120_Hz, frameRateOverrides.at(1234));
+
+ // ExplicitCategory case that expects no global touch boost and thus has frame rate override.
+ layers[0].name = "ExplicitDefault 60";
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::Default;
+ layers[1].name = "ExplicitCategory Normal";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Normal;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ // ExplicitCategory case that expects no global touch boost and thus has frame rate override.
+ layers[0].name = "ExplicitDefault 60";
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::Default;
+ layers[1].name = "ExplicitCategory Low";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::Low;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ // ExplicitCategory case that expects no global touch boost and thus has frame rate override.
+ layers[0].name = "ExplicitDefault 60";
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].frameRateCategory = FrameRateCategory::Default;
+ layers[1].name = "ExplicitCategory NoPreference";
+ layers[1].vote = LayerVoteType::ExplicitCategory;
+ layers[1].desiredRefreshRate = 0_Hz;
+ layers[1].frameRateCategory = FrameRateCategory::NoPreference;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+}
+
TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
if (GetParam() == Config::FrameRateOverride::Disabled) {
return;
@@ -3204,6 +3684,17 @@
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
EXPECT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::ExplicitGte;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
}
TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
deleted file mode 100644
index 27564b2..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "binder/Status.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
-#pragma clang diagnostic pop
-
-#include <gmock/gmock.h>
-
-using aidl::android::hardware::power::IPowerHintSession;
-using aidl::android::hardware::power::SessionConfig;
-using aidl::android::hardware::power::SessionHint;
-using aidl::android::hardware::power::SessionMode;
-using android::binder::Status;
-
-using namespace aidl::android::hardware::power;
-
-namespace android::Hwc2::mock {
-
-class MockIPowerHintSession : public IPowerHintSession {
-public:
- MockIPowerHintSession();
-
- MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, pause, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, resume, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
- MOCK_METHOD(bool, isRemote, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t), (override));
- MOCK_METHOD(ndk::ScopedAStatus, reportActualWorkDuration, (const ::std::vector<WorkDuration>&),
- (override));
- MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
- MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
- MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getSessionConfig, (SessionConfig * _aidl_return), (override));
-};
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index ae41e7e..af1862d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -44,10 +44,10 @@
MOCK_METHOD(HalResult<void>, setBoost, (aidl::android::hardware::power::Boost, int32_t),
(override));
MOCK_METHOD(HalResult<void>, setMode, (aidl::android::hardware::power::Mode, bool), (override));
- MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+ MOCK_METHOD(HalResult<std::shared_ptr<android::power::PowerHintSessionWrapper>>,
createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t),
(override));
- MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+ MOCK_METHOD(HalResult<std::shared_ptr<android::power::PowerHintSessionWrapper>>,
createHintSessionWithConfig,
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos, aidl::android::hardware::power::SessionTag tag,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
similarity index 75%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
index 770bc15..d383283 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * 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.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockIPowerHintSession.h"
+#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
-MockIPowerHintSession::MockIPowerHintSession() = default;
+MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
+ : power::PowerHintSessionWrapper(nullptr) {}
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
new file mode 100644
index 0000000..bc6950c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
@@ -0,0 +1,55 @@
+/*
+ * 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 "binder/Status.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <aidl/android/hardware/power/IPower.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+#pragma clang diagnostic pop
+
+#include <gmock/gmock.h>
+
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
+using android::binder::Status;
+
+using namespace aidl::android::hardware::power;
+
+namespace android::Hwc2::mock {
+
+class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper {
+public:
+ MockPowerHintSessionWrapper();
+
+ MOCK_METHOD(power::HalResult<void>, updateTargetWorkDuration, (int64_t), (override));
+ MOCK_METHOD(power::HalResult<void>, reportActualWorkDuration,
+ (const ::std::vector<WorkDuration>&), (override));
+ MOCK_METHOD(power::HalResult<void>, pause, (), (override));
+ MOCK_METHOD(power::HalResult<void>, resume, (), (override));
+ MOCK_METHOD(power::HalResult<void>, close, (), (override));
+ MOCK_METHOD(power::HalResult<void>, sendHint, (SessionHint), (override));
+ MOCK_METHOD(power::HalResult<void>, setThreads, (const ::std::vector<int32_t>&), (override));
+ MOCK_METHOD(power::HalResult<void>, setMode, (SessionMode, bool), (override));
+ MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override));
+};
+
+} // namespace android::Hwc2::mock