Merge "Change HighHint category to ignore ExplicitDefault" into main
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index bb3faaf..6686717 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
package android.view;
-@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable @RustOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h" rust_type "nativewindow::Surface";
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index ae56cb0..07908ba 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -40,15 +40,12 @@
public:
static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
sp<BinderCallback> cb = sp<BinderCallback>::make();
+ cb->mLooper = looper;
- int binder_fd = -1;
- IPCThreadState::self()->setupPolling(&binder_fd);
- LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
+ IPCThreadState::self()->setupPolling(&cb->mBinderFd);
+ LOG_ALWAYS_FATAL_IF(cb->mBinderFd < 0, "Failed to setupPolling: %d", cb->mBinderFd);
- int ret = looper->addFd(binder_fd,
- Looper::POLL_CALLBACK,
- Looper::EVENT_INPUT,
- cb,
+ int ret = looper->addFd(cb->mBinderFd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, cb,
nullptr /*data*/);
LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");
@@ -59,13 +56,26 @@
IPCThreadState::self()->handlePolledCommands();
return 1; // Continue receiving callbacks.
}
+
+ void repoll() {
+ if (!mLooper->repoll(mBinderFd)) {
+ ALOGE("Failed to repoll binder FD.");
+ }
+ }
+
+private:
+ sp<Looper> mLooper;
+ int mBinderFd = -1;
};
// LooperCallback for IClientCallback
class ClientCallbackCallback : public LooperCallback {
public:
- static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
+ static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper,
+ const sp<ServiceManager>& manager,
+ sp<BinderCallback> binderCallback) {
sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
+ cb->mBinderCallback = binderCallback;
int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
@@ -102,12 +112,15 @@
}
mManager->handleClientCallbacks();
+ mBinderCallback->repoll(); // b/316829336
+
return 1; // Continue receiving callbacks.
}
private:
friend sp<ClientCallbackCallback>;
ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
sp<ServiceManager> mManager;
+ sp<BinderCallback> mBinderCallback;
};
int main(int argc, char** argv) {
@@ -139,8 +152,8 @@
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
- BinderCallback::setupTo(looper);
- ClientCallbackCallback::setupTo(looper, manager);
+ sp<BinderCallback> binderCallback = BinderCallback::setupTo(looper);
+ ClientCallbackCallback::setupTo(looper, manager, binderCallback);
#ifndef VENDORSERVICEMANAGER
if (!SetProperty("servicemanager.ready", "true")) {
diff --git a/include/android/asset_manager.h b/include/android/asset_manager.h
index 2ac7d4d..6420cd0 100644
--- a/include/android/asset_manager.h
+++ b/include/android/asset_manager.h
@@ -29,6 +29,10 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#if defined(__APPLE__)
+typedef off_t off64_t; // Mac OSX does not define off64_t
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 30200c7..08d339b 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -240,7 +240,7 @@
* the actual GPU duration is not measured.
*
* @return 0 on success.
- * EINVAL if session is nullptr or any duration is an invalid number.
+ * EINVAL if any duration is an invalid number.
* EPIPE if communication with the system service has failed.
*/
int APerformanceHint_reportActualWorkDuration2(
@@ -269,7 +269,7 @@
*
* @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
* @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on
- * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive.
+ * CLOCK_MONOTONIC about when the work starts. This timestamp must be greater than zero.
*/
void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration,
int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__);
@@ -278,8 +278,8 @@
* Sets the actual total work duration in nanoseconds.
*
* @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
- * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be
- * positive.
+ * @param actualTotalDurationNanos The actual total work duration in nanoseconds. This number must
+ * be greater than zero.
*/
void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration,
int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__);
@@ -288,8 +288,8 @@
* Sets the actual CPU work duration in nanoseconds.
*
* @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
- * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be
- * positive.
+ * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds. This number must be
+ * greater than zero.
*/
void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration,
int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__);
diff --git a/include/ftl/details/future.h b/include/ftl/details/future.h
index df1323e..8d82e0f 100644
--- a/include/ftl/details/future.h
+++ b/include/ftl/details/future.h
@@ -73,8 +73,18 @@
return std::get<Impl>(self()).get();
}
+ template <class Rep, class Period>
+ std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
+ if (std::holds_alternative<T>(self())) {
+ return std::future_status::ready;
+ }
+
+ return std::get<Impl>(self()).wait_for(timeout_duration);
+ }
+
private:
auto& self() { return static_cast<Self&>(*this).future_; }
+ const auto& self() const { return static_cast<const Self&>(*this).future_; }
};
template <typename Self, typename T>
@@ -90,6 +100,15 @@
return std::get<Impl>(self()).get();
}
+ template <class Rep, class Period>
+ std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
+ if (std::holds_alternative<T>(self())) {
+ return std::future_status::ready;
+ }
+
+ return std::get<Impl>(self()).wait_for(timeout_duration);
+ }
+
private:
const auto& self() const { return static_cast<const Self&>(*this).future_; }
};
diff --git a/include/ftl/future.h b/include/ftl/future.h
index c78f9b7..dad180f 100644
--- a/include/ftl/future.h
+++ b/include/ftl/future.h
@@ -51,6 +51,7 @@
// Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the
// following are defined for either FutureImpl:
using Base::get;
+ using Base::wait_for;
// Attaches a continuation to the future. The continuation is a function that maps T to either R
// or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 750e170..2d64872 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -290,6 +290,17 @@
*/
bool probablyHasInput() const;
+ /* Wait until there is a message in the channel.
+ *
+ * The |timeout| specifies how long to block waiting for an input event to appear. Negative
+ * values are not allowed.
+ *
+ * In some cases returning before timeout expiration can happen without a message available.
+ * This could happen after the channel was closed on the other side. Another possible reason
+ * is incorrect setup of the channel.
+ */
+ void waitForMessage(std::chrono::milliseconds timeout) const;
+
/* Return a new object that has a duplicate of this channel's fd. */
std::unique_ptr<InputChannel> dup() const;
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 9e426d3..c50bc4a 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -62,7 +62,15 @@
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<int64_t> getHintSessionPreferredRate() override;
+ virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(
+ int tgid, int uid) override;
+ virtual HalResult<void> closeSessionChannel(int tgid, int uid) override;
private:
std::mutex mConnectedHalMutex;
@@ -75,7 +83,7 @@
std::shared_ptr<HalWrapper> initHal();
template <typename T>
- HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+ HalResult<T> processHalResult(HalResult<T>&& result, const char* functionName);
};
// -------------------------------------------------------------------------------------------------
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 4e4a1b0..e2da014 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -14,19 +14,22 @@
* limitations under the License.
*/
-#ifndef ANDROID_POWERHALWRAPPER_H
-#define ANDROID_POWERHALWRAPPER_H
+#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 <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
#include <binder/Status.h>
+#include <utility>
+
namespace android {
namespace power {
@@ -42,44 +45,63 @@
template <typename T>
class HalResult {
public:
- static HalResult<T> ok(T value) { return HalResult(value); }
- static HalResult<T> failed(std::string msg) {
- return HalResult(std::move(msg), /* unsupported= */ false);
- }
+ static HalResult<T> 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) {
+ 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(data);
+ return HalResult<T>::ok(std::forward<T>(data));
}
return HalResult<T>::failed(std::string(status.toString8().c_str()));
}
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) {
+ 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(data);
+ 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(data) : HalResult<T>::failed(ret.description());
+ 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, data)
+ 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(); }
@@ -92,8 +114,8 @@
std::string mErrorMessage;
bool mUnsupported;
- explicit HalResult(T value)
- : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+ 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) {}
};
@@ -158,7 +180,15 @@
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<int64_t> getHintSessionPreferredRate() = 0;
+ virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
+ int uid) = 0;
+ virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0;
};
// Empty Power HAL wrapper that ignores all api calls.
@@ -173,11 +203,22 @@
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;
+ 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<int64_t> getHintSessionPreferredRate() override;
+ HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
+ int uid) override;
+ HalResult<void> closeSessionChannel(int tgid, int uid) override;
+
+protected:
+ virtual const char* getUnsupportedMessage();
};
// Wrapper for the HIDL Power HAL v1.0.
-class HidlHalWrapperV1_0 : public HalWrapper {
+class HidlHalWrapperV1_0 : public EmptyHalWrapper {
public:
explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0)
: mHandleV1_0(std::move(handleV1_0)) {}
@@ -186,14 +227,11 @@
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(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) override;
- HalResult<int64_t> getHintSessionPreferredRate() override;
protected:
const sp<hardware::power::V1_0::IPower> mHandleV1_0;
virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data);
+ const char* getUnsupportedMessage();
private:
HalResult<void> setInteractive(bool enabled);
@@ -238,7 +276,7 @@
};
// Wrapper for the AIDL Power HAL.
-class AidlHalWrapper : public HalWrapper {
+class AidlHalWrapper : public EmptyHalWrapper {
public:
explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle)
: mHandle(std::move(handle)) {}
@@ -250,7 +288,19 @@
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;
+ 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<int64_t> getHintSessionPreferredRate() override;
+ HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
+ int uid) override;
+ HalResult<void> closeSessionChannel(int tgid, int uid) override;
+
+protected:
+ const char* getUnsupportedMessage() override;
private:
// Control access to the boost and mode supported arrays.
@@ -274,5 +324,3 @@
}; // namespace power
}; // namespace android
-
-#endif // ANDROID_POWERHALWRAPPER_H
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index ce9cd1c..7da8d51 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -31,6 +31,8 @@
namespace android {
namespace battery {
+#define REPORTED_INVALID_TIMESTAMP_DELTA_MS 60000
+
typedef uint16_t state_t;
template <class T>
@@ -171,8 +173,12 @@
if (timestamp >= lastStateChangeTimestamp) {
states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
} else {
- ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
- (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+ if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
+ ALOGE("setState is called with an earlier timestamp: %lu, "
+ "previous timestamp: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+ }
+
// The accumulated durations have become unreliable. For example, if the timestamp
// sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
// we would get 4000, which is greater than (last - first). This could lead to
@@ -232,8 +238,10 @@
}
}
} else if (timestamp < lastUpdateTimestamp) {
- ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
- (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
+ if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
+ ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
+ }
for (int i = 0; i < stateCount; i++) {
states[i].timeInStateSinceUpdate = 0;
diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp
index 5a245b6..1140639 100644
--- a/libs/ftl/future_test.cpp
+++ b/libs/ftl/future_test.cpp
@@ -102,4 +102,42 @@
decrement_thread.join();
}
+TEST(Future, WaitFor) {
+ using namespace std::chrono_literals;
+ {
+ auto future = ftl::yield(42);
+ // Check that we can wait_for multiple times without invalidating the future
+ EXPECT_EQ(future.wait_for(1s), std::future_status::ready);
+ EXPECT_EQ(future.wait_for(1s), std::future_status::ready);
+ EXPECT_EQ(future.get(), 42);
+ }
+
+ {
+ std::condition_variable cv;
+ std::mutex m;
+ bool ready = false;
+
+ std::packaged_task<int32_t()> get_int([&] {
+ std::unique_lock lk(m);
+ cv.wait(lk, [&] { return ready; });
+ return 24;
+ });
+
+ auto get_future = ftl::Future(get_int.get_future());
+ std::thread get_thread(std::move(get_int));
+
+ EXPECT_EQ(get_future.wait_for(0s), std::future_status::timeout);
+ {
+ std::unique_lock lk(m);
+ ready = true;
+ }
+ cv.notify_one();
+
+ EXPECT_EQ(get_future.wait_for(1s), std::future_status::ready);
+ EXPECT_EQ(get_future.get(), 24);
+
+ get_thread.join();
+ }
+}
+
} // namespace android::test
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 8d18551..83c2b7f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -56,6 +56,7 @@
#include <android-base/thread_annotations.h>
#include <gui/LayerStatePermissions.h>
+#include <gui/ScreenCaptureResults.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
@@ -3138,11 +3139,19 @@
}
status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener) {
+ const sp<IScreenCaptureListener>& captureListener,
+ bool sync) {
sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
- binder::Status status = s->captureLayers(captureArgs, captureListener);
+ binder::Status status;
+ if (sync) {
+ gui::ScreenCaptureResults captureResults;
+ status = s->captureLayersSync(captureArgs, &captureResults);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ } else {
+ status = s->captureLayers(captureArgs, captureListener);
+ }
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index e3122bc..51e0193 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -46,6 +46,7 @@
import android.gui.LayerDebugInfo;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
+import android.gui.ScreenCaptureResults;
import android.gui.ARect;
import android.gui.SchedulingPolicy;
import android.gui.StalledTransactionInfo;
@@ -245,6 +246,16 @@
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
* This requires READ_FRAME_BUFFER permission. This function will fail if there
+ * is a secure window on screen. This is a blocking call and will return the
+ * ScreenCaptureResults, including the captured buffer. Because this is blocking, the
+ * caller doesn't care about the fence and the binder thread in SurfaceFlinger will wait
+ * on the fence to fire before returning the results.
+ */
+ ScreenCaptureResults captureLayersSync(in LayerCaptureArgs args);
+
+ /**
+ * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
+ * This requires READ_FRAME_BUFFER permission. This function will fail if there
* is a secure window on screen
*/
oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index c952ba2..2bdbd43 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -104,6 +104,8 @@
(int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override));
MOCK_METHOD(binder::Status, captureLayers,
(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
+ MOCK_METHOD(binder::Status, captureLayersSync,
+ (const LayerCaptureArgs&, gui::ScreenCaptureResults*), (override));
MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override));
MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override));
MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&),
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e1dc791..6b8e824 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -416,26 +416,36 @@
};
struct DisplayState {
- enum {
+ enum : uint32_t {
eSurfaceChanged = 0x01,
eLayerStackChanged = 0x02,
eDisplayProjectionChanged = 0x04,
eDisplaySizeChanged = 0x08,
- eFlagsChanged = 0x10
+ eFlagsChanged = 0x10,
+
+ eAllChanged = ~0u
};
+ // Not for direct use. Prefer constructor below for new displays.
DisplayState();
+
+ DisplayState(sp<IBinder> token, ui::LayerStack layerStack)
+ : what(eAllChanged),
+ token(std::move(token)),
+ layerStack(layerStack),
+ layerStackSpaceRect(Rect::INVALID_RECT),
+ orientedDisplaySpaceRect(Rect::INVALID_RECT) {}
+
void merge(const DisplayState& other);
void sanitize(int32_t permissions);
uint32_t what = 0;
uint32_t flags = 0;
sp<IBinder> token;
- sp<IGraphicBufferProducer> surface;
ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
- // These states define how layers are projected onto the physical display.
+ // These states define how layers are projected onto the physical or virtual display.
//
// Layers are first clipped to `layerStackSpaceRect'. They are then translated and
// scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated
@@ -446,10 +456,17 @@
// will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers
// will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
// 0).
+ //
+ // Rect::INVALID_RECT sizes the space to the active resolution of the physical display, or the
+ // default dimensions of the virtual display surface.
+ //
ui::Rotation orientation = ui::ROTATION_0;
Rect layerStackSpaceRect = Rect::EMPTY_RECT;
Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
+ // Exclusive to virtual displays: The sink surface into which the virtual display is rendered,
+ // and an optional resolution that overrides its default dimensions.
+ sp<IGraphicBufferProducer> surface;
uint32_t width = 0;
uint32_t height = 0;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 14e3dd5..88a2c34 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -849,7 +849,8 @@
static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
static status_t captureDisplay(DisplayId, const gui::CaptureArgs&,
const sp<IScreenCaptureListener>&);
- static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
+ static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&,
+ bool sync);
[[deprecated]] static status_t captureDisplay(DisplayId id,
const sp<IScreenCaptureListener>& listener) {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index d4b8dbe..b9ab803 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -168,8 +168,8 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
- InputEvent *consumeEvent(int timeoutMs = 3000) {
- waitForEventAvailable(timeoutMs);
+ InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
+ mClientChannel->waitForMessage(timeout);
InputEvent *ev;
uint32_t seqId;
@@ -302,15 +302,6 @@
t.apply(true);
}
-private:
- void waitForEventAvailable(int timeoutMs) {
- struct pollfd fd;
-
- fd.fd = mClientChannel->getFd();
- fd.events = POLLIN;
- poll(&fd, 1, timeoutMs);
- }
-
public:
sp<SurfaceControl> mSurfaceControl;
std::shared_ptr<InputChannel> mClientChannel;
@@ -615,7 +606,7 @@
// A tap within the surface but outside the touchable region should not be sent to the surface.
injectTap(20, 30);
- EXPECT_EQ(surface->consumeEvent(200 /*timeoutMs*/), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr);
injectTap(31, 52);
surface->expectTap(20, 30);
@@ -981,12 +972,12 @@
obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
obscuringSurface->showAt(100, 100);
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
@@ -1002,12 +993,12 @@
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
@@ -1024,12 +1015,12 @@
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
@@ -1046,12 +1037,12 @@
injectTap(111, 111);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) {
@@ -1076,12 +1067,12 @@
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) {
@@ -1116,7 +1107,7 @@
// Does not receive events outside its crop
injectTap(26, 26);
- EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
+ EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
}
/**
@@ -1141,7 +1132,7 @@
// Does not receive events outside parent bounds
injectTap(31, 31);
- EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
+ EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
}
/**
@@ -1167,7 +1158,7 @@
// Does not receive events outside crop layer bounds
injectTap(21, 21);
injectTap(71, 71);
- EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
+ EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) {
@@ -1184,7 +1175,7 @@
[&](auto &t, auto &sc) { t.reparent(sc, parent->mSurfaceControl); });
injectTap(101, 101);
- EXPECT_EQ(parent->consumeEvent(100), nullptr);
+ EXPECT_EQ(parent->consumeEvent(/*timeout=*/100ms), nullptr);
}
class MultiDisplayTests : public InputSurfacesTest {
@@ -1233,7 +1224,7 @@
// Touches should be dropped if the layer is on an invalid display.
injectTapOnDisplay(101, 101, layerStack.id);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
// However, we still let the window be focused and receive keys.
surface->requestFocus(layerStack.id);
@@ -1271,12 +1262,12 @@
injectTapOnDisplay(101, 101, layerStack.id);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
surface->requestFocus(layerStack.id);
surface->assertFocusChange(true);
injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
}
TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c6ea317..577d239 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -791,6 +791,10 @@
return binder::Status::ok();
}
+ binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults*) override {
+ return binder::Status::ok();
+ }
+
binder::Status captureLayers(const LayerCaptureArgs&,
const sp<IScreenCaptureListener>&) override {
return binder::Status::ok();
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index fa094df..37de00c 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -521,11 +521,11 @@
bool InputChannel::probablyHasInput() const {
struct pollfd pfds = {.fd = mFd, .events = POLLIN};
if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) {
- // This can be a false negative because EAGAIN and ENOMEM are not handled. The latter should
- // be extremely rare. The EAGAIN is also unlikely because it happens only when the signal
- // arrives while the syscall is executed, and the syscall is quick. Hitting EAGAIN too often
+ // This can be a false negative because EINTR and ENOMEM are not handled. The latter should
+ // be extremely rare. The EINTR is also unlikely because it happens only when the signal
+ // arrives while the syscall is executed, and the syscall is quick. Hitting EINTR too often
// would be a sign of having too many signals, which is a bigger performance problem. A
- // common tradition is to repeat the syscall on each EAGAIN, but it is not necessary here.
+ // common tradition is to repeat the syscall on each EINTR, but it is not necessary here.
// In other words, the missing one liner is replaced by a multiline explanation.
return false;
}
@@ -534,6 +534,22 @@
return (pfds.revents & POLLIN) != 0;
}
+void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const {
+ if (timeout < 0ms) {
+ LOG(FATAL) << "Timeout cannot be negative, received " << timeout.count();
+ }
+ struct pollfd pfds = {.fd = mFd, .events = POLLIN};
+ int ret;
+ std::chrono::time_point<std::chrono::steady_clock> stopTime =
+ std::chrono::steady_clock::now() + timeout;
+ std::chrono::milliseconds remaining = timeout;
+ do {
+ ret = ::poll(&pfds, /*nfds=*/1, /*timeout=*/remaining.count());
+ remaining = std::chrono::duration_cast<std::chrono::milliseconds>(
+ stopTime - std::chrono::steady_clock::now());
+ } while (ret == -1 && errno == EINTR && remaining > 0ms);
+}
+
std::unique_ptr<InputChannel> InputChannel::dup() const {
base::unique_fd newFd(dupFd());
return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 1baeb26..0c850fe 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -64,13 +64,6 @@
}
flag {
- name: "remove_app_switch_drops"
- namespace: "input"
- description: "Remove the logic of dropping events due to pending app switch"
- bug: "284808102"
-}
-
-flag {
name: "disable_reject_touch_on_stylus_hover"
namespace: "input"
description: "Disable touch rejection when the stylus hovers the screen"
@@ -104,3 +97,10 @@
description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones"
bug: "315313622"
}
+
+flag {
+ name: "rate_limit_user_activity_poke_in_dispatcher"
+ namespace: "input"
+ description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher."
+ bug: "320499729"
+}
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index d4278be..8056d9a 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -29,6 +29,7 @@
#define ANDROID_DATA_SPACE_H
#include <inttypes.h>
+#include <stdint.h>
#include <sys/cdefs.h>
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index aab7df0..22ad834 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,6 +16,8 @@
extern crate nativewindow_bindgen as ffi;
+pub mod surface;
+
pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
use binder::{
@@ -210,7 +212,7 @@
}
impl Debug for HardwareBuffer {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("HardwareBuffer").field("id", &self.id()).finish()
}
}
diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs
new file mode 100644
index 0000000..25fea80
--- /dev/null
+++ b/libs/nativewindow/rust/src/surface.rs
@@ -0,0 +1,143 @@
+// 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.
+
+//! Rust wrapper for `ANativeWindow` and related types.
+
+use binder::{
+ binder_impl::{BorrowedParcel, UnstructuredParcelable},
+ impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
+ unstable_api::{status_result, AsNative},
+ StatusCode,
+};
+use nativewindow_bindgen::{
+ AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire, ANativeWindow_getFormat,
+ ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_readFromParcel,
+ ANativeWindow_release, ANativeWindow_writeToParcel,
+};
+use std::error::Error;
+use std::fmt::{self, Debug, Display, Formatter};
+use std::ptr::{null_mut, NonNull};
+
+/// Wrapper around an opaque C `ANativeWindow`.
+#[derive(PartialEq, Eq)]
+pub struct Surface(NonNull<ANativeWindow>);
+
+impl Surface {
+ /// Returns the current width in pixels of the window surface.
+ pub fn width(&self) -> Result<u32, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let width = unsafe { ANativeWindow_getWidth(self.0.as_ptr()) };
+ width.try_into().map_err(|_| ErrorCode(width))
+ }
+
+ /// Returns the current height in pixels of the window surface.
+ pub fn height(&self) -> Result<u32, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let height = unsafe { ANativeWindow_getHeight(self.0.as_ptr()) };
+ height.try_into().map_err(|_| ErrorCode(height))
+ }
+
+ /// Returns the current pixel format of the window surface.
+ pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let format = unsafe { ANativeWindow_getFormat(self.0.as_ptr()) };
+ format.try_into().map_err(|_| ErrorCode(format))
+ }
+}
+
+impl Drop for Surface {
+ fn drop(&mut self) {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ unsafe { ANativeWindow_release(self.0.as_ptr()) }
+ }
+}
+
+impl Debug for Surface {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_struct("Surface")
+ .field("width", &self.width())
+ .field("height", &self.height())
+ .field("format", &self.format())
+ .finish()
+ }
+}
+
+impl Clone for Surface {
+ fn clone(&self) -> Self {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ unsafe { ANativeWindow_acquire(self.0.as_ptr()) };
+ Self(self.0)
+ }
+}
+
+impl UnstructuredParcelable for Surface {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ let status =
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ unsafe { ANativeWindow_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ }
+
+ fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ let mut buffer = null_mut();
+
+ let status =
+ // SAFETY: Both pointers must be valid because they are obtained from references.
+ // `ANativeWindow_readFromParcel` doesn't store them or do anything else special
+ // with them. If it returns success then it will have allocated a new
+ // `ANativeWindow` and incremented the reference count, so we can use it until we
+ // release it.
+ unsafe { ANativeWindow_readFromParcel(parcel.as_native(), &mut buffer) };
+
+ status_result(status)?;
+
+ Ok(Self(
+ NonNull::new(buffer)
+ .expect("ANativeWindow_readFromParcel returned success but didn't allocate buffer"),
+ ))
+ }
+}
+
+impl_deserialize_for_unstructured_parcelable!(Surface);
+impl_serialize_for_unstructured_parcelable!(Surface);
+
+// SAFETY: The underlying *ANativeWindow can be moved between threads.
+unsafe impl Send for Surface {}
+
+// SAFETY: The underlying *ANativeWindow can be used from multiple threads concurrently.
+unsafe impl Sync for Surface {}
+
+/// An error code returned by methods on [`Surface`].
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct ErrorCode(i32);
+
+impl Error for ErrorCode {}
+
+impl Display for ErrorCode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Error {}", self.0)
+ }
+}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
index 4525a42..5689f7d 100644
--- a/libs/nativewindow/rust/sys/nativewindow_bindings.h
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -19,3 +19,4 @@
#include <android/hardware_buffer_aidl.h>
#include <android/hdr_metadata.h>
#include <android/native_window.h>
+#include <android/native_window_aidl.h>
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 3e1ac33..233134d 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -28,28 +28,28 @@
namespace renderengine {
std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
- switch (args.renderEngineType) {
- case RenderEngineType::SKIA_GL:
+ if (args.threaded == Threaded::YES) {
+ switch (args.graphicsApi) {
+ case GraphicsApi::GL:
+ ALOGD("Threaded RenderEngine with SkiaGL Backend");
+ return renderengine::threaded::RenderEngineThreaded::create([args]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(args);
+ });
+ case GraphicsApi::VK:
+ ALOGD("Threaded RenderEngine with SkiaVK Backend");
+ return renderengine::threaded::RenderEngineThreaded::create([args]() {
+ return android::renderengine::skia::SkiaVkRenderEngine::create(args);
+ });
+ }
+ }
+
+ switch (args.graphicsApi) {
+ case GraphicsApi::GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
- case RenderEngineType::SKIA_VK:
+ case GraphicsApi::VK:
ALOGD("RenderEngine with SkiaVK Backend");
return renderengine::skia::SkiaVkRenderEngine::create(args);
- case RenderEngineType::SKIA_GL_THREADED: {
- ALOGD("Threaded RenderEngine with SkiaGL Backend");
- return renderengine::threaded::RenderEngineThreaded::create(
- [args]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(args);
- },
- args.renderEngineType);
- }
- case RenderEngineType::SKIA_VK_THREADED:
- ALOGD("Threaded RenderEngine with SkiaVK Backend");
- return renderengine::threaded::RenderEngineThreaded::create(
- [args]() {
- return android::renderengine::skia::SkiaVkRenderEngine::create(args);
- },
- args.renderEngineType);
}
}
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index a7f1df9..101f519 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -30,46 +30,6 @@
using namespace android::renderengine;
///////////////////////////////////////////////////////////////////////////////
-// Helpers for Benchmark::Apply
-///////////////////////////////////////////////////////////////////////////////
-
-std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
- switch (type) {
- case RenderEngine::RenderEngineType::SKIA_GL_THREADED:
- return "skiaglthreaded";
- case RenderEngine::RenderEngineType::SKIA_GL:
- return "skiagl";
- case RenderEngine::RenderEngineType::SKIA_VK:
- return "skiavk";
- case RenderEngine::RenderEngineType::SKIA_VK_THREADED:
- return "skiavkthreaded";
- }
-}
-
-/**
- * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a
- * Benchmark which specifies which RenderEngineType it uses.
- *
- * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make
- * it obvious which version is being run.
- *
- * @param b The benchmark family
- * @param type The type of RenderEngine to use.
- */
-static void AddRenderEngineType(benchmark::internal::Benchmark* b,
- RenderEngine::RenderEngineType type) {
- b->Arg(static_cast<int64_t>(type));
- b->ArgName(RenderEngineTypeName(type));
-}
-
-/**
- * Run a benchmark once using SKIA_GL_THREADED.
- */
-static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) {
- AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED);
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Helpers for calling drawLayers
///////////////////////////////////////////////////////////////////////////////
@@ -104,7 +64,8 @@
return std::pair<uint32_t, uint32_t>(width, height);
}
-static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) {
+static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded,
+ RenderEngine::GraphicsApi graphicsApi) {
auto args = RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
@@ -112,7 +73,8 @@
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
- .setRenderEngineType(type)
+ .setThreaded(threaded)
+ .setGraphicsApi(graphicsApi)
.build();
return RenderEngine::create(args);
}
@@ -214,8 +176,11 @@
// Benchmarks
///////////////////////////////////////////////////////////////////////////////
-void BM_blur(benchmark::State& benchState) {
- auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range()));
+template <class... Args>
+void BM_blur(benchmark::State& benchState, Args&&... args) {
+ auto args_tuple = std::make_tuple(std::move(args)...);
+ auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
+ static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
// Initially use cpu access so we can decode into it with AImageDecoder.
auto [width, height] = getDisplaySize();
@@ -259,4 +224,5 @@
benchDrawLayers(*re, layers, benchState, "blurred");
}
-BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded);
+BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 818d035..7047358 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -33,7 +33,7 @@
#include <memory>
/**
- * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported).
+ * Allows to override the RenderEngine backend.
*/
#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
@@ -92,11 +92,14 @@
REALTIME = 4,
};
- enum class RenderEngineType {
- SKIA_GL = 3,
- SKIA_GL_THREADED = 4,
- SKIA_VK = 5,
- SKIA_VK_THREADED = 6,
+ enum class Threaded {
+ NO,
+ YES,
+ };
+
+ enum class GraphicsApi {
+ GL,
+ VK,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
@@ -176,10 +179,9 @@
// query is required to be thread safe.
virtual bool supportsBackgroundBlur() = 0;
- // Returns the current type of RenderEngine instance that was created.
// TODO(b/180767535): This is only implemented to allow for backend-specific behavior, which
// we should not allow in general, so remove this.
- RenderEngineType getRenderEngineType() const { return mRenderEngineType; }
+ bool isThreaded() const { return mThreaded == Threaded::YES; }
static void validateInputBufferUsage(const sp<GraphicBuffer>&);
static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
@@ -191,9 +193,9 @@
virtual void setEnableTracing(bool /*tracingEnabled*/) {}
protected:
- RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {}
+ RenderEngine() : RenderEngine(Threaded::NO) {}
- RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
+ RenderEngine(Threaded threaded) : mThreaded(threaded) {}
// Maps GPU resources for this buffer.
// Note that work may be deferred to an additional thread, i.e. this call
@@ -228,7 +230,7 @@
friend class impl::ExternalTexture;
friend class threaded::RenderEngineThreaded;
friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
- const RenderEngineType mRenderEngineType;
+ const Threaded mThreaded;
// Update protectedContext mode depending on whether or not any layer has a protected buffer.
void updateProtectedContext(const std::vector<LayerSettings>&,
@@ -251,7 +253,8 @@
bool precacheToneMapperShaderOnly;
bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
- RenderEngine::RenderEngineType renderEngineType;
+ RenderEngine::Threaded threaded;
+ RenderEngine::GraphicsApi graphicsApi;
struct Builder;
@@ -261,14 +264,16 @@
bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
bool _supportsBackgroundBlur,
RenderEngine::ContextPriority _contextPriority,
- RenderEngine::RenderEngineType _renderEngineType)
+ RenderEngine::Threaded _threaded,
+ RenderEngine::GraphicsApi _graphicsApi)
: pixelFormat(_pixelFormat),
imageCacheSize(_imageCacheSize),
enableProtectedContext(_enableProtectedContext),
precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
supportsBackgroundBlur(_supportsBackgroundBlur),
contextPriority(_contextPriority),
- renderEngineType(_renderEngineType) {}
+ threaded(_threaded),
+ graphicsApi(_graphicsApi) {}
RenderEngineCreationArgs() = delete;
};
@@ -299,14 +304,18 @@
this->contextPriority = contextPriority;
return *this;
}
- Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
- this->renderEngineType = renderEngineType;
+ Builder& setThreaded(RenderEngine::Threaded threaded) {
+ this->threaded = threaded;
+ return *this;
+ }
+ Builder& setGraphicsApi(RenderEngine::GraphicsApi graphicsApi) {
+ this->graphicsApi = graphicsApi;
return *this;
}
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext,
precacheToneMapperShaderOnly, supportsBackgroundBlur,
- contextPriority, renderEngineType);
+ contextPriority, threaded, graphicsApi);
}
private:
@@ -317,8 +326,8 @@
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
- RenderEngine::RenderEngineType renderEngineType =
- RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+ RenderEngine::Threaded threaded = RenderEngine::Threaded::YES;
+ RenderEngine::GraphicsApi graphicsApi = RenderEngine::GraphicsApi::GL;
};
} // namespace renderengine
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index d688b51..fea4129 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -268,7 +268,7 @@
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
- : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
+ : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat),
args.supportsBackgroundBlur),
mEGLDisplay(display),
mEGLContext(ctxt),
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 3729be6..330cc66 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -269,9 +269,9 @@
SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled);
}
-SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat,
+SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat,
bool supportsBackgroundBlur)
- : RenderEngine(type), mDefaultPixelFormat(pixelFormat) {
+ : RenderEngine(threaded), mDefaultPixelFormat(pixelFormat) {
if (supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
mBlurFilter = new KawaseBlurFilter();
@@ -389,10 +389,9 @@
void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
// Only run this if RE is running on its own thread. This
- // way the access to GL operations is guaranteed to be happening on the
+ // way the access to GL/VK operations is guaranteed to be happening on the
// same thread.
- if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED &&
- mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) {
+ if (!isThreaded()) {
return;
}
// We don't attempt to map a buffer if the buffer contains protected content. In GL this is
@@ -1017,7 +1016,7 @@
.fakeOutputDataspace = fakeDataspace}));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
- static constexpr float kDimmingThreshold = 0.2f;
+ static constexpr float kDimmingThreshold = 0.9f;
// ...or we're rendering an HDR layer down to an 8-bit target
// Most HDR standards require at least 10-bits of color depth for source content, so we
// can just extract the transfer function rather than dig into precise gralloc layout.
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index ac134af..e88d44c 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -59,7 +59,7 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur);
+ SkiaRenderEngine(Threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur);
~SkiaRenderEngine() override;
std::future<void> primeCache(bool shouldPrimeUltraHDR) override final;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index ba20d1f..3af85c0 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -596,7 +596,7 @@
}
SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
- : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
+ : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat),
args.supportsBackgroundBlur) {}
SkiaVkRenderEngine::~SkiaVkRenderEngine() {
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 11d4fde..4c18704 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -106,92 +106,46 @@
virtual ~RenderEngineFactory() = default;
virtual std::string name() = 0;
- virtual renderengine::RenderEngine::RenderEngineType type() = 0;
- virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
- virtual bool typeSupported() = 0;
+ virtual renderengine::RenderEngine::GraphicsApi graphicsApi() = 0;
+ virtual bool apiSupported() = 0;
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() {
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setThreaded(renderengine::RenderEngine::Threaded::NO)
+ .setGraphicsApi(graphicsApi())
+ .build();
+ return renderengine::RenderEngine::create(reCreationArgs);
+ }
};
class SkiaVkRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaVkRenderEngineFactory"; }
- renderengine::RenderEngine::RenderEngineType type() {
- return renderengine::RenderEngine::RenderEngineType::SKIA_VK;
+ renderengine::RenderEngine::GraphicsApi graphicsApi() override {
+ return renderengine::RenderEngine::GraphicsApi::VK;
}
- std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
- std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine();
- return re;
- }
-
- std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() {
- renderengine::RenderEngineCreationArgs reCreationArgs =
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(type())
- .build();
- return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs);
- }
-
- bool typeSupported() override {
+ bool apiSupported() override {
return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine();
}
- void skip() { GTEST_SKIP(); }
};
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
- renderengine::RenderEngine::RenderEngineType type() {
- return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ renderengine::RenderEngine::GraphicsApi graphicsApi() {
+ return renderengine::RenderEngine::GraphicsApi::GL;
}
- std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
- renderengine::RenderEngineCreationArgs reCreationArgs =
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(type())
- .build();
- return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
- }
-
- bool typeSupported() override { return true; }
-};
-
-class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
-public:
- std::string name() override { return "SkiaGLCMRenderEngineFactory"; }
-
- renderengine::RenderEngine::RenderEngineType type() {
- return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
- }
-
- std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
- renderengine::RenderEngineCreationArgs reCreationArgs =
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .setRenderEngineType(type())
- .build();
- return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
- }
-
- bool typeSupported() override { return true; }
+ bool apiSupported() override { return true; }
};
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -1526,7 +1480,7 @@
std::make_shared<SkiaVkRenderEngineFactory>()));
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1534,7 +1488,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1561,7 +1515,7 @@
}
TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1595,7 +1549,7 @@
}
TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1616,7 +1570,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1624,7 +1578,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1632,7 +1586,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1640,7 +1594,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1648,7 +1602,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1656,7 +1610,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1664,7 +1618,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1672,7 +1626,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1680,7 +1634,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1688,7 +1642,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1696,7 +1650,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1706,7 +1660,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported()) {
+ if (!renderEngineFactory->apiSupported()) {
GTEST_SKIP();
}
@@ -1717,7 +1671,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported()) {
+ if (!renderEngineFactory->apiSupported()) {
GTEST_SKIP();
}
@@ -1726,7 +1680,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1734,7 +1688,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1742,7 +1696,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1750,7 +1704,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1758,7 +1712,7 @@
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1766,7 +1720,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1774,7 +1728,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1782,7 +1736,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1790,7 +1744,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1798,7 +1752,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1806,7 +1760,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1814,7 +1768,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1822,7 +1776,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1830,7 +1784,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1838,7 +1792,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1846,7 +1800,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1856,7 +1810,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported()) {
+ if (!renderEngineFactory->apiSupported()) {
GTEST_SKIP();
}
@@ -1867,7 +1821,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported()) {
+ if (!renderEngineFactory->apiSupported()) {
GTEST_SKIP();
}
@@ -1876,7 +1830,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1884,7 +1838,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1892,7 +1846,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1900,7 +1854,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1908,7 +1862,7 @@
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1916,7 +1870,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1924,7 +1878,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1932,7 +1886,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1940,7 +1894,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1948,7 +1902,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1956,7 +1910,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1964,7 +1918,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1972,7 +1926,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1980,7 +1934,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1988,7 +1942,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1996,7 +1950,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2006,7 +1960,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported()) {
+ if (!renderEngineFactory->apiSupported()) {
GTEST_SKIP();
}
@@ -2017,7 +1971,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported()) {
+ if (!renderEngineFactory->apiSupported()) {
GTEST_SKIP();
}
@@ -2026,7 +1980,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2034,7 +1988,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2042,7 +1996,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2050,7 +2004,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2058,7 +2012,7 @@
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2066,7 +2020,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2074,7 +2028,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2082,7 +2036,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2090,7 +2044,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2108,7 +2062,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2131,7 +2085,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2155,7 +2109,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2180,7 +2134,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2206,7 +2160,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2235,7 +2189,7 @@
}
TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2271,7 +2225,7 @@
if (mRE->canSkipPostRenderCleanup()) {
// Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so
// it never gets added to the cleanup list. In those cases, we can skip.
- EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK);
+ EXPECT_TRUE(GetParam()->graphicsApi() == renderengine::RenderEngine::GraphicsApi::VK);
} else {
mRE->cleanupPostRender();
EXPECT_TRUE(mRE->canSkipPostRenderCleanup());
@@ -2279,7 +2233,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersCrop) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2332,7 +2286,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2380,7 +2334,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2417,7 +2371,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersXY) {
- if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -2460,7 +2414,7 @@
}
TEST_P(RenderEngineTest, testClear) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2492,7 +2446,7 @@
}
TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2543,7 +2497,7 @@
}
TEST_P(RenderEngineTest, testBorder) {
- if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -2588,7 +2542,7 @@
}
TEST_P(RenderEngineTest, testDimming) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2663,7 +2617,7 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2741,7 +2695,7 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2804,7 +2758,7 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2868,7 +2822,7 @@
}
TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2922,7 +2876,7 @@
}
TEST_P(RenderEngineTest, test_isOpaque) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2972,7 +2926,7 @@
}
TEST_P(RenderEngineTest, test_tonemapPQMatches) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -2989,7 +2943,7 @@
}
TEST_P(RenderEngineTest, test_tonemapHLGMatches) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -3006,7 +2960,7 @@
}
TEST_P(RenderEngineTest, r8_behaves_as_mask) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -3066,7 +3020,7 @@
}
TEST_P(RenderEngineTest, r8_respects_color_transform) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -3131,7 +3085,7 @@
}
TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -3199,7 +3153,7 @@
}
TEST_P(RenderEngineTest, primeShaderCache) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 1b9adba..d56dbb2 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -35,8 +35,7 @@
void SetUp() override {
mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
- [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); },
- renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED);
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
}
std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index f58f543..f4cebc0 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -33,13 +33,12 @@
namespace renderengine {
namespace threaded {
-std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory,
- RenderEngineType type) {
- return std::make_unique<RenderEngineThreaded>(std::move(factory), type);
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory));
}
-RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type)
- : RenderEngine(type) {
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory)
+ : RenderEngine(Threaded::YES) {
ATRACE_CALL();
std::lock_guard lockThread(mThreadMutex);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 3f1e67f..d440c96 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -37,10 +37,9 @@
*/
class RenderEngineThreaded : public RenderEngine {
public:
- static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory,
- RenderEngineType type);
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
- RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
+ RenderEngineThreaded(CreateInstanceFactory factory);
~RenderEngineThreaded() override;
std::future<void> primeCache(bool shouldPrimeUltraHDR) override;
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index d992aa5..cc92bc3 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,6 +21,18 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "libsensor_flags",
+ package: "com.android.hardware.libsensor.flags",
+ srcs: ["libsensor_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "libsensor_flags_c_lib",
+ host_supported: true,
+ aconfig_declarations: "libsensor_flags",
+}
+
cc_library {
name: "libsensor",
@@ -52,6 +64,10 @@
"android.companion.virtual.virtualdevice_aidl-cpp",
],
+ static_libs: [
+ "libsensor_flags_c_lib",
+ ],
+
export_include_dirs: ["include"],
export_shared_lib_headers: [
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index b82a79f..9411e20 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -37,6 +37,8 @@
#include <sensor/Sensor.h>
#include <sensor/SensorEventQueue.h>
+#include <com_android_hardware_libsensor_flags.h>
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -192,6 +194,9 @@
}
status_t SensorManager::assertStateLocked() {
+#if COM_ANDROID_HARDWARE_LIBSENSOR_FLAGS(SENSORMANAGER_PING_BINDER)
+ if (mSensorServer == nullptr) {
+#else
bool initSensorManager = false;
if (mSensorServer == nullptr) {
initSensorManager = true;
@@ -203,6 +208,7 @@
}
}
if (initSensorManager) {
+#endif
waitForSensorService(&mSensorServer);
LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL");
diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig
new file mode 100644
index 0000000..a8b38e9
--- /dev/null
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.hardware.libsensor.flags"
+
+flag {
+ name: "sensormanager_ping_binder"
+ namespace: "libsensors"
+ description: "Whether to pingBinder on SensorManager init"
+ bug: "322228259"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 72c6f1a..1ada5e5 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -118,6 +118,15 @@
}
}
+void InputFilter::setAccessibilitySlowKeysThreshold(nsecs_t threshold) {
+ std::scoped_lock _l(mLock);
+
+ if (mConfig.slowKeysThresholdNs != threshold) {
+ mConfig.slowKeysThresholdNs = threshold;
+ notifyConfigurationChangedLocked();
+ }
+}
+
void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index 153d29d..4ddc9f4 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -35,6 +35,7 @@
*/
virtual void dump(std::string& dump) = 0;
virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
+ virtual void setAccessibilitySlowKeysThreshold(nsecs_t threshold) = 0;
virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0;
};
@@ -61,6 +62,7 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override;
+ void setAccessibilitySlowKeysThreshold(nsecs_t threshold) override;
void setAccessibilityStickyKeysEnabled(bool enabled) override;
void dump(std::string& dump) override;
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
index a8759b7..6c31442 100644
--- a/services/inputflinger/InputFilterCallbacks.cpp
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -17,6 +17,11 @@
#define LOG_TAG "InputFilterCallbacks"
#include "InputFilterCallbacks.h"
+#include <aidl/com/android/server/inputflinger/BnInputThread.h>
+#include <android/binder_auto_utils.h>
+#include <utils/StrongPointer.h>
+#include <utils/Thread.h>
+#include <functional>
namespace android {
@@ -29,6 +34,47 @@
event.scanCode, event.metaState, event.downTime);
}
+namespace {
+
+using namespace aidl::com::android::server::inputflinger;
+
+class InputFilterThreadImpl : public Thread {
+public:
+ explicit InputFilterThreadImpl(std::function<void()> loop)
+ : Thread(/*canCallJava=*/true), mThreadLoop(loop) {}
+
+ ~InputFilterThreadImpl() {}
+
+private:
+ std::function<void()> mThreadLoop;
+
+ bool threadLoop() override {
+ mThreadLoop();
+ return true;
+ }
+};
+
+class InputFilterThread : public BnInputThread {
+public:
+ InputFilterThread(std::shared_ptr<IInputThreadCallback> callback) : mCallback(callback) {
+ mThread = sp<InputFilterThreadImpl>::make([this]() { loopOnce(); });
+ mThread->run("InputFilterThread", ANDROID_PRIORITY_URGENT_DISPLAY);
+ }
+
+ ndk::ScopedAStatus finish() override {
+ mThread->requestExit();
+ return ndk::ScopedAStatus::ok();
+ }
+
+private:
+ sp<Thread> mThread;
+ std::shared_ptr<IInputThreadCallback> mCallback;
+
+ void loopOnce() { LOG_ALWAYS_FATAL_IF(!mCallback->loopOnce().isOk()); }
+};
+
+} // namespace
+
InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener,
InputFilterPolicyInterface& policy)
: mNextListener(listener), mPolicy(policy) {}
@@ -49,6 +95,13 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus InputFilterCallbacks::createInputFilterThread(
+ const std::shared_ptr<IInputThreadCallback>& callback,
+ std::shared_ptr<IInputThread>* aidl_return) {
+ *aidl_return = ndk::SharedRefBase::make<InputFilterThread>(callback);
+ return ndk::ScopedAStatus::ok();
+}
+
uint32_t InputFilterCallbacks::getModifierState() {
std::scoped_lock _l(mLock);
return mStickyModifierState.modifierState;
diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h
index 31c160a..a74955b 100644
--- a/services/inputflinger/InputFilterCallbacks.h
+++ b/services/inputflinger/InputFilterCallbacks.h
@@ -19,6 +19,7 @@
#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
#include <android/binder_auto_utils.h>
#include <utils/Mutex.h>
+#include <memory>
#include <mutex>
#include "InputFilterPolicyInterface.h"
#include "InputListener.h"
@@ -31,6 +32,9 @@
using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+using aidl::com::android::server::inputflinger::IInputThread;
+using IInputThreadCallback =
+ aidl::com::android::server::inputflinger::IInputThread::IInputThreadCallback;
class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks {
public:
@@ -53,6 +57,9 @@
ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override;
ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState,
int32_t lockedModifierState) override;
+ ndk::ScopedAStatus createInputFilterThread(
+ const std::shared_ptr<IInputThreadCallback>& callback,
+ std::shared_ptr<IInputThread>* aidl_return) override;
};
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 4571ef4..3ac4285 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -109,7 +109,9 @@
const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
pc.move(deltaX, deltaY);
- pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ if (canUnfadeOnDisplay(displayId)) {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
const auto [x, y] = pc.getPosition();
NotifyMotionArgs newArgs(args);
@@ -131,7 +133,9 @@
const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
pc.move(deltaX, deltaY);
- pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ if (canUnfadeOnDisplay(displayId)) {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
const auto [x, y] = pc.getPosition();
newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -140,7 +144,9 @@
newArgs.yCursorPosition = y;
} else {
// This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
- pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ if (canUnfadeOnDisplay(displayId)) {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
const auto [x, y] = pc.getPosition();
for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
@@ -223,7 +229,7 @@
if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
- } else {
+ } else if (canUnfadeOnDisplay(args.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
}
@@ -323,6 +329,10 @@
return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
}
+bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) {
+ return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
+}
+
void PointerChoreographer::updatePointerControllersLocked() {
std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
std::set<DeviceId> touchDevicesToKeep;
@@ -342,7 +352,7 @@
mMousePointersByDisplay.try_emplace(displayId,
getMouseControllerConstructor(displayId));
auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId());
- if (isNewMouseDevice || isNewMousePointer) {
+ if ((isNewMouseDevice || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
}
@@ -513,6 +523,28 @@
return true;
}
+void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
+ std::scoped_lock lock(mLock);
+ if (visible) {
+ mDisplaysWithPointersHidden.erase(displayId);
+ // We do not unfade the icons here, because we don't know when the last event happened.
+ return;
+ }
+
+ mDisplaysWithPointersHidden.emplace(displayId);
+
+ // Hide any icons that are currently visible on the display.
+ if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
+ const auto& [_, controller] = *it;
+ controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+ for (const auto& [_, controller] : mStylusPointersByDevice) {
+ if (controller->getDisplayId() == displayId) {
+ controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+ }
+}
+
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
int32_t displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index f46419e..6aab3aa 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -67,6 +67,11 @@
*/
virtual bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
int32_t displayId, DeviceId deviceId) = 0;
+ /**
+ * Set whether pointer icons for mice, touchpads, and styluses should be visible on the
+ * given display.
+ */
+ virtual void setPointerIconVisibility(int32_t displayId, bool visible) = 0;
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
@@ -89,6 +94,7 @@
void setStylusPointerIconEnabled(bool enabled) override;
bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
int32_t displayId, DeviceId deviceId) override;
+ void setPointerIconVisibility(int32_t displayId, bool visible) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
@@ -110,6 +116,7 @@
std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked(
int32_t associatedDisplayId) REQUIRES(mLock);
InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
+ bool canUnfadeOnDisplay(int32_t displayId) REQUIRES(mLock);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
@@ -143,6 +150,7 @@
std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
bool mShowTouchesEnabled GUARDED_BY(mLock);
bool mStylusPointerIconEnabled GUARDED_BY(mLock);
+ std::set<int32_t /*displayId*/> mDisplaysWithPointersHidden;
};
} // namespace android
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 2921d30..994d1c4 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -17,6 +17,8 @@
package com.android.server.inputflinger;
import com.android.server.inputflinger.DeviceInfo;
+import com.android.server.inputflinger.IInputThread;
+import com.android.server.inputflinger.IInputThread.IInputThreadCallback;
import com.android.server.inputflinger.InputFilterConfiguration;
import com.android.server.inputflinger.KeyEvent;
@@ -36,6 +38,9 @@
/** Sends back modifier state */
void onModifierStateChanged(int modifierState, int lockedModifierState);
+
+ /** Creates an Input filter thread */
+ IInputThread createInputFilterThread(in IInputThreadCallback callback);
}
/** Returns if InputFilter is enabled */
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
new file mode 100644
index 0000000..2f6b8fc
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package com.android.server.inputflinger;
+
+/** Interface to handle and run things on an InputThread
+ * Exposes main functionality of InputThread.h to rust which internally used system/core/libutils
+ * infrastructure.
+ *
+ * <p>
+ * NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't
+ * have JNI support and can't call into Java policy that we use currently. libutils provided
+ * Thread.h also recommends against using std::thread and using the provided infrastructure that
+ * already provides way of attaching JniEnv to the created thread. So, we are using this interface
+ * to expose the InputThread infrastructure to rust.
+ * </p>
+ * TODO(b/321769871): Implement the threading infrastructure with JniEnv support in rust
+ */
+interface IInputThread {
+ /** Finish input thread (if not running, this call does nothing) */
+ void finish();
+
+ /** Callbacks from C++ to call into inputflinger rust components */
+ interface IInputThreadCallback {
+ /**
+ * The created thread will keep looping and calling this function.
+ * It's the responsibility of RUST component to appropriately put the thread to sleep and
+ * wake according to the use case.
+ */
+ void loopOnce();
+ }
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
index 38b1612..9984a6a 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
@@ -22,6 +22,8 @@
parcelable InputFilterConfiguration {
// Threshold value for Bounce keys filter (check bounce_keys_filter.rs)
long bounceKeysThresholdNs;
- // If sticky keys filter is enabled
+ // If sticky keys filter is enabled (check sticky_keys_filter.rs)
boolean stickyKeysEnabled;
+ // Threshold value for Slow keys filter (check slow_keys_filter.rs)
+ long slowKeysThresholdNs;
}
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index c889b9b..fe33d94 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -98,13 +98,6 @@
constexpr bool DEBUG_TOUCH_OCCLUSION = true;
/**
- * Log debug messages about the app switch latency optimization.
- * Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart)
- */
-const bool DEBUG_APP_SWITCH =
- android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "AppSwitch");
-
-/**
* Log debug messages about hover events.
* Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart)
*/
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index cc0d49c..2153d8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -164,6 +164,11 @@
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
+std::ostream& operator<<(std::ostream& out, const KeyEntry& keyEntry) {
+ out << keyEntry.getDescription();
+ return out;
+}
+
// --- TouchModeEntry ---
TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index e2e13c3..a915805 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -18,6 +18,7 @@
#include "InjectionState.h"
#include "InputTarget.h"
+#include "trace/EventTrackerInterface.h"
#include <gui/InputApplication.h>
#include <input/Input.h>
@@ -125,6 +126,7 @@
int32_t scanCode;
int32_t metaState;
nsecs_t downTime;
+ std::unique_ptr<trace::EventTrackerInterface> traceTracker;
bool syntheticRepeat; // set to true for synthetic key repeats
@@ -147,6 +149,8 @@
std::string getDescription() const override;
};
+std::ostream& operator<<(std::ostream& out, const KeyEntry& motionEntry);
+
struct MotionEntry : EventEntry {
int32_t deviceId;
uint32_t source;
@@ -165,6 +169,7 @@
nsecs_t downTime;
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
+ std::unique_ptr<trace::EventTrackerInterface> traceTracker;
size_t getPointerCount() const { return pointerProperties.size(); }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e998d91..c349a58 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -35,7 +35,6 @@
#include <input/PrintTools.h>
#include <input/TraceTools.h>
#include <openssl/mem.h>
-#include <powermanager/PowerManager.h>
#include <unistd.h>
#include <utils/Trace.h>
@@ -72,12 +71,17 @@
using android::os::InputEventInjectionSync;
namespace input_flags = com::android::input::flags;
-// TODO(b/312714754): remove the corresponding code, as well.
-static const bool REMOVE_APP_SWITCH_DROPS = true;
-
namespace android::inputdispatcher {
namespace {
+
+template <class Entry>
+void ensureEventTraced(const Entry& entry) {
+ if (!entry.traceTracker) {
+ LOG(FATAL) << "Expected event entry to be traced, but it wasn't: " << entry;
+ }
+}
+
// Temporarily releases a held mutex for the lifetime of the instance.
// Named to match std::scoped_lock
class scoped_unlock {
@@ -95,10 +99,8 @@
android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
HwTimeoutMultiplier());
-// Amount of time to allow for all pending events to be processed when an app switch
-// key is on the way. This is used to preempt input dispatch and drop input events
-// when an application takes too long to respond and the user has pressed an app switch key.
-constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
+// The default minimum time gap between two user activity poke events.
+const std::chrono::milliseconds DEFAULT_USER_ACTIVITY_POKE_INTERVAL = 100ms;
const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier();
@@ -778,17 +780,39 @@
return {};
}
+int32_t getUserActivityEventType(const EventEntry& eventEntry) {
+ switch (eventEntry.type) {
+ case EventEntry::Type::KEY: {
+ return USER_ACTIVITY_EVENT_BUTTON;
+ }
+ case EventEntry::Type::MOTION: {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+ if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
+ return USER_ACTIVITY_EVENT_TOUCH;
+ }
+ return USER_ACTIVITY_EVENT_OTHER;
+ }
+ default: {
+ LOG_ALWAYS_FATAL("%s events are not user activity",
+ ftl::enum_string(eventEntry.type).c_str());
+ }
+ }
+}
+
} // namespace
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
+ : InputDispatcher(policy, nullptr) {}
+
+InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
+ std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
: mPolicy(policy),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
- mAppSwitchSawKeyDown(false),
- mAppSwitchDueTime(LLONG_MAX),
+ mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
mNextUnblockedEvent(nullptr),
mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
@@ -807,6 +831,12 @@
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
#endif
mKeyRepeatState.lastKeyEntry = nullptr;
+
+ if (traceBackend) {
+ // TODO: Create input tracer instance.
+ }
+
+ mLastUserActivityTimes.fill(0);
}
InputDispatcher::~InputDispatcher() {
@@ -975,28 +1005,10 @@
return;
}
- // Optimize latency of app switches.
- // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
- // been pressed. When it expires, we preempt dispatch and drop all other pending events.
- bool isAppSwitchDue;
- if (!REMOVE_APP_SWITCH_DROPS) {
- isAppSwitchDue = mAppSwitchDueTime <= currentTime;
- nextWakeupTime = std::min(nextWakeupTime, mAppSwitchDueTime);
- }
-
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
- if (!REMOVE_APP_SWITCH_DROPS) {
- if (isAppSwitchDue) {
- // The inbound queue is empty so the app switch key we were waiting
- // for will never arrive. Stop waiting for it.
- resetPendingAppSwitchLocked(false);
- isAppSwitchDue = false;
- }
- }
-
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
@@ -1091,16 +1103,6 @@
case EventEntry::Type::KEY: {
std::shared_ptr<const KeyEntry> keyEntry =
std::static_pointer_cast<const KeyEntry>(mPendingEvent);
- if (!REMOVE_APP_SWITCH_DROPS) {
- if (isAppSwitchDue) {
- if (isAppSwitchKeyEvent(*keyEntry)) {
- resetPendingAppSwitchLocked(true);
- isAppSwitchDue = false;
- } else if (dropReason == DropReason::NOT_DROPPED) {
- dropReason = DropReason::APP_SWITCH;
- }
- }
- }
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
dropReason = DropReason::STALE;
}
@@ -1108,17 +1110,16 @@
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
+ if (done && mTracer) {
+ ensureEventTraced(*keyEntry);
+ mTracer->eventProcessingComplete(*keyEntry->traceTracker);
+ }
break;
}
case EventEntry::Type::MOTION: {
std::shared_ptr<const MotionEntry> motionEntry =
std::static_pointer_cast<const MotionEntry>(mPendingEvent);
- if (!REMOVE_APP_SWITCH_DROPS) {
- if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
- dropReason = DropReason::APP_SWITCH;
- }
- }
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
// The event is stale. However, only drop stale events if there isn't an ongoing
// gesture. That would allow us to complete the processing of the current stroke.
@@ -1138,17 +1139,17 @@
}
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
+ if (done && mTracer) {
+ ensureEventTraced(*motionEntry);
+ mTracer->eventProcessingComplete(*motionEntry->traceTracker);
+ }
break;
}
case EventEntry::Type::SENSOR: {
std::shared_ptr<const SensorEntry> sensorEntry =
std::static_pointer_cast<const SensorEntry>(mPendingEvent);
- if (!REMOVE_APP_SWITCH_DROPS) {
- if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
- dropReason = DropReason::APP_SWITCH;
- }
- }
+
// Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use
// 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead.
nsecs_t bootTime = systemTime(SYSTEM_TIME_BOOTTIME);
@@ -1245,27 +1246,12 @@
case EventEntry::Type::KEY: {
LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
"Unexpected untrusted event.");
- // Optimize app switch latency.
- // If the application takes too long to catch up then we drop all events preceding
- // the app switch key.
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
- if (!REMOVE_APP_SWITCH_DROPS) {
- if (isAppSwitchKeyEvent(keyEntry)) {
- if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
- if (DEBUG_APP_SWITCH) {
- ALOGD("App switch is pending!");
- }
- mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
- needWake = true;
- }
- }
- }
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ if (mTracer) {
+ ensureEventTraced(keyEntry);
}
+
// If a new up event comes in, and the pending event with same key code has been asked
// to try again later because of the policy. We have to reset the intercept key wake up
// time for it may have been handled in the policy and could be dropped.
@@ -1286,7 +1272,11 @@
case EventEntry::Type::MOTION: {
LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
"Unexpected untrusted event.");
- if (shouldPruneInboundQueueLocked(static_cast<const MotionEntry&>(entry))) {
+ const auto& motionEntry = static_cast<const MotionEntry&>(entry);
+ if (mTracer) {
+ ensureEventTraced(motionEntry);
+ }
+ if (shouldPruneInboundQueueLocked(motionEntry)) {
mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
@@ -1401,10 +1391,6 @@
}
reason = "inbound event was dropped because input dispatch is disabled";
break;
- case DropReason::APP_SWITCH:
- ALOGI("Dropped event because of pending overdue app switch.");
- reason = "inbound event was dropped because of pending overdue app switch";
- break;
case DropReason::BLOCKED:
LOG(INFO) << "Dropping because the current application is not responding and the user "
"has started interacting with a different application: "
@@ -1468,33 +1454,6 @@
}
}
-static bool isAppSwitchKeyCode(int32_t keyCode) {
- return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL ||
- keyCode == AKEYCODE_APP_SWITCH;
-}
-
-bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) {
- return !(keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry.keyCode) &&
- (keyEntry.policyFlags & POLICY_FLAG_TRUSTED) &&
- (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER);
-}
-
-bool InputDispatcher::isAppSwitchPendingLocked() const {
- return mAppSwitchDueTime != LLONG_MAX;
-}
-
-void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
- mAppSwitchDueTime = LLONG_MAX;
-
- if (DEBUG_APP_SWITCH) {
- if (handled) {
- ALOGD("App switch has arrived.");
- } else {
- ALOGD("App switch was abandoned.");
- }
- }
-}
-
bool InputDispatcher::haveCommandsLocked() const {
return !mCommandQueue.empty();
}
@@ -1567,6 +1526,10 @@
entry->repeatCount + 1, entry->downTime);
newEntry->syntheticRepeat = true;
+ if (mTracer) {
+ newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
+ }
+
mKeyRepeatState.lastKeyEntry = newEntry;
mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
return newEntry;
@@ -1873,6 +1836,13 @@
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
+ if (mTracer) {
+ ensureEventTraced(*entry);
+ for (const auto& target : inputTargets) {
+ mTracer->dispatchToTargetHint(*entry->traceTracker, target);
+ }
+ }
+
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
@@ -1998,6 +1968,13 @@
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
+ if (mTracer) {
+ ensureEventTraced(*entry);
+ for (const auto& target : inputTargets) {
+ mTracer->dispatchToTargetHint(*entry->traceTracker, target);
+ }
+ }
+
// Dispatch the motion.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
@@ -3187,6 +3164,21 @@
// Not poking user activity if the event type does not represent a user activity
return;
}
+
+ const int32_t eventType = getUserActivityEventType(eventEntry);
+ if (input_flags::rate_limit_user_activity_poke_in_dispatcher()) {
+ // Note that we're directly getting the time diff between the current event and the previous
+ // event. This is assuming that the first user event always happens at a timestamp that is
+ // greater than `mMinTimeBetweenUserActivityPokes` (otherwise, the first user event will
+ // wrongly be dropped). In real life, `mMinTimeBetweenUserActivityPokes` is a much smaller
+ // value than the potential first user activity event time, so this is ok.
+ std::chrono::nanoseconds timeSinceLastEvent =
+ std::chrono::nanoseconds(eventEntry.eventTime - mLastUserActivityTimes[eventType]);
+ if (timeSinceLastEvent < mMinTimeBetweenUserActivityPokes) {
+ return;
+ }
+ }
+
int32_t displayId = getTargetDisplayId(eventEntry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
const WindowInfo* windowDisablingUserActivityInfo = nullptr;
@@ -3197,7 +3189,6 @@
}
}
- int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
switch (eventEntry.type) {
case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
@@ -3211,9 +3202,6 @@
}
return;
}
- if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
- eventType = USER_ACTIVITY_EVENT_TOUCH;
- }
break;
}
case EventEntry::Type::KEY: {
@@ -3237,7 +3225,6 @@
return;
}
- eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
default: {
@@ -3247,6 +3234,7 @@
}
}
+ mLastUserActivityTimes[eventType] = eventEntry.eventTime;
auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -3635,6 +3623,7 @@
PointerCoords scaledCoords[MAX_POINTERS];
const PointerCoords* usingCoords = motionEntry.pointerCoords.data();
+ // TODO(b/316355518): Do not modify coords before dispatch.
// Set the X and Y offset and X and Y scale depending on the input source.
if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) {
@@ -3710,6 +3699,9 @@
keyEntry.keyCode, keyEntry.scanCode,
keyEntry.metaState, keyEntry.repeatCount,
keyEntry.downTime, keyEntry.eventTime);
+ if (mTracer) {
+ mTracer->traceEventDispatch(*dispatchEntry, keyEntry.traceTracker.get());
+ }
break;
}
@@ -3718,7 +3710,11 @@
LOG(INFO) << "Publishing " << *dispatchEntry << " to "
<< connection->getInputChannelName();
}
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
status = publishMotionEvent(*connection, *dispatchEntry);
+ if (mTracer) {
+ mTracer->traceEventDispatch(*dispatchEntry, motionEntry.traceTracker.get());
+ }
break;
}
@@ -4400,6 +4396,9 @@
args.deviceId, args.source, args.displayId, policyFlags,
args.action, flags, keyCode, args.scanCode, metaState,
repeatCount, args.downTime);
+ if (mTracer) {
+ newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
+ }
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
@@ -4526,6 +4525,9 @@
args.yPrecision, args.xCursorPosition,
args.yCursorPosition, args.downTime,
args.pointerProperties, args.pointerCoords);
+ if (mTracer) {
+ newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
+ }
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4713,6 +4715,9 @@
incomingKey.getScanCode(), metaState,
incomingKey.getRepeatCount(),
incomingKey.getDownTime());
+ if (mTracer) {
+ injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry);
+ }
injectedEntries.push(std::move(injectedEntry));
break;
}
@@ -4770,6 +4775,9 @@
samplePointerCoords +
pointerCount));
transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
+ if (mTracer) {
+ injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry);
+ }
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
@@ -5319,6 +5327,14 @@
resetNoFocusedWindowTimeoutLocked();
}
+void InputDispatcher::setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) {
+ if (interval.count() < 0) {
+ LOG_ALWAYS_FATAL("Minimum time between user activity pokes should be >= 0");
+ }
+ std::scoped_lock _l(mLock);
+ mMinTimeBetweenUserActivityPokes = interval;
+}
+
/**
* Sets the focused display, which is responsible for receiving focus-dispatched input events where
* the display not specified.
@@ -5843,16 +5859,6 @@
dump += INDENT "Connections: <none>\n";
}
- dump += "input_flags::remove_app_switch_drops() = ";
- dump += toString(REMOVE_APP_SWITCH_DROPS);
- dump += "\n";
- if (isAppSwitchPendingLocked()) {
- dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
- ns2ms(mAppSwitchDueTime - now()));
- } else {
- dump += INDENT "AppSwitch: not pending\n";
- }
-
if (!mTouchModePerDisplay.empty()) {
dump += INDENT "TouchModePerDisplay:\n";
for (const auto& [displayId, touchMode] : mTouchModePerDisplay) {
@@ -5869,6 +5875,8 @@
ns2ms(mConfig.keyRepeatTimeout));
dump += mLatencyTracker.dump(INDENT2);
dump += mLatencyAggregator.dump(INDENT2);
+ dump += INDENT "InputTracer: ";
+ dump += mTracer == nullptr ? "Disabled" : "Enabled";
}
void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 3567288..e635852 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -32,6 +32,8 @@
#include "Monitor.h"
#include "TouchState.h"
#include "TouchedWindow.h"
+#include "trace/InputTracerInterface.h"
+#include "trace/InputTracingBackendInterface.h"
#include <attestation/HmacKeyManager.h>
#include <gui/InputApplication.h>
@@ -39,6 +41,7 @@
#include <input/Input.h>
#include <input/InputTransport.h>
#include <limits.h>
+#include <powermanager/PowerManager.h>
#include <stddef.h>
#include <unistd.h>
#include <utils/BitSet.h>
@@ -82,6 +85,9 @@
static constexpr bool kDefaultInTouchMode = true;
explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
+ // Constructor used for testing.
+ explicit InputDispatcher(InputDispatcherPolicyInterface&,
+ std::unique_ptr<trace::InputTracingBackendInterface>);
~InputDispatcher() override;
void dump(std::string& dump) const override;
@@ -111,6 +117,7 @@
int32_t displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
void setFocusedDisplay(int32_t displayId) override;
+ void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) override;
void setInputDispatchMode(bool enabled, bool frozen) override;
void setInputFilterEnabled(bool enabled) override;
bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
@@ -155,7 +162,6 @@
enum class DropReason {
NOT_DROPPED,
POLICY,
- APP_SWITCH,
DISABLED,
BLOCKED,
STALE,
@@ -172,6 +178,9 @@
std::condition_variable mDispatcherIsAlive;
mutable std::condition_variable mDispatcherEnteredIdle;
+ // Input event tracer. The tracer will only exist on builds where input tracing is allowed.
+ std::unique_ptr<trace::InputTracerInterface> mTracer GUARDED_BY(mLock);
+
sp<Looper> mLooper;
std::shared_ptr<const EventEntry> mPendingEvent GUARDED_BY(mLock);
@@ -204,6 +213,11 @@
int64_t mWindowInfosVsyncId GUARDED_BY(mLock);
+ std::chrono::milliseconds mMinTimeBetweenUserActivityPokes GUARDED_BY(mLock);
+
+ /** Stores the latest user-activity poke event times per user activity types. */
+ std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock);
+
// With each iteration, InputDispatcher nominally processes one queued event,
// a timeout, or a response from an input consumer.
// This method should only be called on the input dispatcher's own thread.
@@ -228,14 +242,6 @@
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(std::shared_ptr<const EventEntry> entry) REQUIRES(mLock);
- // App switch latency optimization.
- bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
- nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
-
- bool isAppSwitchKeyEvent(const KeyEntry& keyEntry);
- bool isAppSwitchPendingLocked() const REQUIRES(mLock);
- void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
-
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 001dc6c..c8f3d05 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -101,6 +101,9 @@
*/
virtual void setFocusedDisplay(int32_t displayId) = 0;
+ /** Sets the minimum time between user activity pokes. */
+ virtual void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) = 0;
+
/* Sets the input dispatching mode.
*
* This method may be called on any thread (usually by the input manager).
diff --git a/services/inputflinger/dispatcher/trace/EventTrackerInterface.h b/services/inputflinger/dispatcher/trace/EventTrackerInterface.h
new file mode 100644
index 0000000..929820e
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/EventTrackerInterface.h
@@ -0,0 +1,32 @@
+/*
+ * 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
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * A tracker used to track the lifecycle of a traced input event.
+ * The tracker should be stored inside the traced event. When the event goes out of scope after
+ * the dispatcher has finished processing it, the tracker will call back into the tracer to
+ * initiate cleanup.
+ */
+class EventTrackerInterface {
+public:
+ virtual ~EventTrackerInterface() = default;
+};
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
new file mode 100644
index 0000000..c6cd7de
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -0,0 +1,87 @@
+/*
+ * 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 "../Entry.h"
+#include "../InputTarget.h"
+#include "EventTrackerInterface.h"
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * InputTracerInterface is the tracing interface for InputDispatcher.
+ *
+ * The tracer is responsible for tracing information about input events and where they are
+ * dispatched. The trace is logged to the backend using the InputTracingBackendInterface.
+ *
+ * A normal traced event should have the following lifecycle:
+ * - The EventTracker is obtained from traceInboundEvent(), after which point the event
+ * should not change.
+ * - While the event is being processed, dispatchToTargetHint() is called for each target that
+ * the event will be eventually sent to.
+ * - Once all targets have been determined, eventProcessingComplete() is called, at which point
+ * the tracer will have enough information to commit the event to the trace.
+ * - For each event that is dispatched to the client, traceEventDispatch() is called, and the
+ * tracer will record that the event was sent to the client.
+ */
+class InputTracerInterface {
+public:
+ InputTracerInterface() = default;
+ virtual ~InputTracerInterface() = default;
+ InputTracerInterface(const InputTracerInterface&) = delete;
+ InputTracerInterface& operator=(const InputTracerInterface&) = delete;
+
+ /**
+ * Trace an input event that is being processed by InputDispatcher. The event must not be
+ * modified after it is traced to keep the traced event consistent with the event that is
+ * eventually dispatched. An EventTracker is returned for each traced event that should be used
+ * to track the event's lifecycle inside InputDispatcher.
+ */
+ virtual std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) = 0;
+
+ /**
+ * Notify the tracer that the traced event will be sent to the given InputTarget.
+ * The tracer may change how the event is logged depending on the target. For example,
+ * events targeting certain UIDs may be logged as sensitive events.
+ * This may be called 0 or more times for each tracked event before event processing is
+ * completed.
+ */
+ virtual void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) = 0;
+
+ /**
+ * Notify the tracer that the event processing is complete. This may be called at most once
+ * for each traced event. If a tracked event is dropped before it can be processed, it is
+ * possible that this is never called before the EventTracker is destroyed.
+ *
+ * This is used to commit the event to the trace in a timely manner, rather than always
+ * waiting for the event to go out of scope (and thus for the EventTracker to be destroyed)
+ * before committing. The point at which the event is destroyed can depend on several factors
+ * outside of our control, such as how long apps take to respond, so we don't want to depend on
+ * that.
+ */
+ virtual void eventProcessingComplete(const EventTrackerInterface&) = 0;
+
+ /**
+ * Trace an input event being successfully dispatched to a window. The dispatched event may
+ * be a previously traced inbound event, or it may be a synthesized event that has not been
+ * previously traced. For inbound events that were previously traced, the EventTracker cookie
+ * must be provided. For events that were not previously traced, the cookie must be null.
+ */
+ virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) = 0;
+};
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
new file mode 100644
index 0000000..b430a5b
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -0,0 +1,51 @@
+/*
+ * 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 "../Entry.h"
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * An interface for the tracing backend, used for setting a custom backend for testing.
+ */
+class InputTracingBackendInterface {
+public:
+ virtual ~InputTracingBackendInterface() = default;
+
+ /** Trace a KeyEvent. */
+ virtual void traceKeyEvent(const KeyEntry&) const = 0;
+
+ /** Trace a MotionEvent. */
+ virtual void traceMotionEvent(const MotionEntry&) const = 0;
+
+ /** Trace an event being sent to a window. */
+ struct WindowDispatchArgs {
+ std::variant<MotionEntry, KeyEntry> eventEntry;
+ nsecs_t deliveryTime;
+ int32_t resolvedFlags;
+ gui::Uid targetUid;
+ int64_t vsyncId;
+ int32_t windowId;
+ ui::Transform transform;
+ ui::Transform rawTransform;
+ std::array<uint8_t, 32> hmac;
+ };
+ virtual void traceWindowDispatch(const WindowDispatchArgs&) const = 0;
+};
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 40359a4..9abfef1 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -131,10 +131,10 @@
// Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
int32_t mousePointerSpeed;
- // Whether to apply an acceleration curve to pointer movements from mice.
+ // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
//
// Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
- bool mousePointerAccelerationEnabled;
+ std::set<int32_t> displaysWithMousePointerAccelerationDisabled;
// Velocity control parameters for mouse pointer movements.
//
@@ -243,7 +243,7 @@
InputReaderConfiguration()
: virtualKeyQuietTime(0),
mousePointerSpeed(0),
- mousePointerAccelerationEnabled(true),
+ displaysWithMousePointerAccelerationDisabled(),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
static_cast<float>(
android::os::IInputConstants::
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 4cebd64..d207ed1 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -159,15 +159,15 @@
out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
- if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) ||
- configurePointerCapture) {
- configureOnChangePointerSpeed(readerConfig);
- }
-
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) ||
configurePointerCapture) {
configureOnChangeDisplayInfo(readerConfig);
}
+
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) ||
+ configurePointerCapture) {
+ configureOnChangePointerSpeed(readerConfig);
+ }
return out;
}
@@ -510,7 +510,8 @@
} else {
if (mEnableNewMousePointerBallistics) {
mNewPointerVelocityControl.setAccelerationEnabled(
- config.mousePointerAccelerationEnabled);
+ config.displaysWithMousePointerAccelerationDisabled.count(
+ mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0);
mNewPointerVelocityControl.setCurve(
createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
} else {
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
index f79219f..9ec02a6 100644
--- a/services/inputflinger/reader/mapper/SlopController.cpp
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -54,11 +54,13 @@
mCumulativeValue += value;
if (abs(mCumulativeValue) >= mSlopThreshold) {
+ ALOGD("SlopController: did not drop event with value .%3f", value);
mHasSlopBeenMet = true;
// Return the amount of value that exceeds the slop.
return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
}
+ ALOGD("SlopController: dropping event with value .%3f", value);
return 0;
}
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 2803805..9e6dbe4 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -42,6 +42,7 @@
"libbinder_rs",
"liblog_rust",
"liblogger",
+ "libnix",
],
host_supported: true,
}
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
index 894b881..2d5039a 100644
--- a/services/inputflinger/rust/bounce_keys_filter.rs
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -118,6 +118,10 @@
}
self.next.notify_devices_changed(device_infos);
}
+
+ fn destroy(&mut self) {
+ self.next.destroy();
+ }
}
#[cfg(test)]
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index e94a71f..a544fa3 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -22,11 +22,14 @@
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo,
IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ IInputThread::{IInputThread, IInputThreadCallback::IInputThreadCallback},
InputFilterConfiguration::InputFilterConfiguration,
KeyEvent::KeyEvent,
};
use crate::bounce_keys_filter::BounceKeysFilter;
+use crate::input_filter_thread::InputFilterThread;
+use crate::slow_keys_filter::SlowKeysFilter;
use crate::sticky_keys_filter::StickyKeysFilter;
use log::{error, info};
use std::sync::{Arc, Mutex, RwLock};
@@ -35,6 +38,7 @@
pub trait Filter {
fn notify_key(&mut self, event: &KeyEvent);
fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]);
+ fn destroy(&mut self);
}
struct InputFilterState {
@@ -50,6 +54,7 @@
// Access to mutable references to mutable state (includes access to filters, enabled, etc.) is
// guarded by Mutex for thread safety
state: Mutex<InputFilterState>,
+ input_filter_thread: InputFilterThread,
}
impl Interface for InputFilter {}
@@ -67,7 +72,11 @@
first_filter: Box<dyn Filter + Send + Sync>,
callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
) -> InputFilter {
- Self { callbacks, state: Mutex::new(InputFilterState { first_filter, enabled: false }) }
+ Self {
+ callbacks: callbacks.clone(),
+ state: Mutex::new(InputFilterState { first_filter, enabled: false }),
+ input_filter_thread: InputFilterThread::new(InputFilterThreadCreator::new(callbacks)),
+ }
}
}
@@ -89,24 +98,36 @@
}
fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> {
- let mut state = self.state.lock().unwrap();
- let mut first_filter: Box<dyn Filter + Send + Sync> =
- Box::new(BaseFilter::new(self.callbacks.clone()));
- if config.stickyKeysEnabled {
- first_filter = Box::new(StickyKeysFilter::new(
- first_filter,
- ModifierStateListener::new(self.callbacks.clone()),
- ));
- state.enabled = true;
- info!("Sticky keys filter is installed");
+ {
+ let mut state = self.state.lock().unwrap();
+ state.first_filter.destroy();
+ let mut first_filter: Box<dyn Filter + Send + Sync> =
+ Box::new(BaseFilter::new(self.callbacks.clone()));
+ if config.stickyKeysEnabled {
+ first_filter = Box::new(StickyKeysFilter::new(
+ first_filter,
+ ModifierStateListener::new(self.callbacks.clone()),
+ ));
+ state.enabled = true;
+ info!("Sticky keys filter is installed");
+ }
+ if config.slowKeysThresholdNs > 0 {
+ first_filter = Box::new(SlowKeysFilter::new(
+ first_filter,
+ config.slowKeysThresholdNs,
+ self.input_filter_thread.clone(),
+ ));
+ state.enabled = true;
+ info!("Slow keys filter is installed");
+ }
+ if config.bounceKeysThresholdNs > 0 {
+ first_filter =
+ Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
+ state.enabled = true;
+ info!("Bounce keys filter is installed");
+ }
+ state.first_filter = first_filter;
}
- if config.bounceKeysThresholdNs > 0 {
- first_filter =
- Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
- state.enabled = true;
- info!("Bounce keys filter is installed");
- }
- state.first_filter = first_filter;
Result::Ok(())
}
}
@@ -132,27 +153,51 @@
fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
// do nothing
}
+
+ fn destroy(&mut self) {
+ // do nothing
+ }
}
-pub struct ModifierStateListener {
- callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
-}
+/// This struct wraps around IInputFilterCallbacks restricting access to only
+/// {@code onModifierStateChanged()} method of the callback.
+#[derive(Clone)]
+pub struct ModifierStateListener(Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>);
impl ModifierStateListener {
- /// Create a new InputFilter instance.
pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener {
- Self { callbacks }
+ Self(callbacks)
}
pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) {
let _ = self
- .callbacks
+ .0
.read()
.unwrap()
.onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32);
}
}
+/// This struct wraps around IInputFilterCallbacks restricting access to only
+/// {@code createInputFilterThread()} method of the callback.
+#[derive(Clone)]
+pub struct InputFilterThreadCreator(Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>);
+
+impl InputFilterThreadCreator {
+ pub fn new(
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ ) -> InputFilterThreadCreator {
+ Self(callbacks)
+ }
+
+ pub fn create(
+ &self,
+ input_thread_callback: &Strong<dyn IInputThreadCallback>,
+ ) -> Strong<dyn IInputThread> {
+ self.0.read().unwrap().createInputFilterThread(input_thread_callback).unwrap()
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::input_filter::{
@@ -218,7 +263,7 @@
let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
bounceKeysThresholdNs: 100,
- stickyKeysEnabled: false,
+ ..Default::default()
});
assert!(result.is_ok());
let result = input_filter.isEnabled();
@@ -231,8 +276,8 @@
let test_callbacks = TestCallbacks::new();
let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
- bounceKeysThresholdNs: 0,
stickyKeysEnabled: true,
+ ..Default::default()
});
assert!(result.is_ok());
let result = input_filter.isEnabled();
@@ -240,6 +285,33 @@
assert!(result.unwrap());
}
+ #[test]
+ fn test_notify_configuration_changed_enabled_slow_keys() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ slowKeysThresholdNs: 100,
+ ..Default::default()
+ });
+ assert!(result.is_ok());
+ let result = input_filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_notify_configuration_changed_destroys_existing_filters() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
+ let _ = input_filter
+ .notifyConfigurationChanged(&InputFilterConfiguration { ..Default::default() });
+ assert!(test_filter.is_destroy_called());
+ }
+
fn create_key_event() -> KeyEvent {
KeyEvent {
id: 1,
@@ -271,6 +343,7 @@
struct TestFilterInner {
is_device_changed_called: bool,
last_event: Option<KeyEvent>,
+ is_destroy_called: bool,
}
#[derive(Default, Clone)]
@@ -296,6 +369,10 @@
pub fn is_device_changed_called(&self) -> bool {
self.0.read().unwrap().is_device_changed_called
}
+
+ pub fn is_destroy_called(&self) -> bool {
+ self.0.read().unwrap().is_destroy_called
+ }
}
impl Filter for TestFilter {
@@ -305,14 +382,19 @@
fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
self.inner().is_device_changed_called = true;
}
+ fn destroy(&mut self) {
+ self.inner().is_destroy_called = true;
+ }
}
}
#[cfg(test)]
pub mod test_callbacks {
- use binder::Interface;
+ use binder::{BinderFeatures, Interface, Strong};
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
- IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent,
+ IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+ IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback},
+ KeyEvent::KeyEvent,
};
use std::sync::{Arc, RwLock, RwLockWriteGuard};
@@ -321,6 +403,7 @@
last_modifier_state: u32,
last_locked_modifier_state: u32,
last_event: Option<KeyEvent>,
+ test_thread: Option<TestThread>,
}
#[derive(Default, Clone)]
@@ -354,6 +437,17 @@
pub fn get_last_locked_modifier_state(&self) -> u32 {
self.0.read().unwrap().last_locked_modifier_state
}
+
+ pub fn is_thread_created(&self) -> bool {
+ self.0.read().unwrap().test_thread.is_some()
+ }
+
+ pub fn is_thread_finished(&self) -> bool {
+ if let Some(test_thread) = &self.0.read().unwrap().test_thread {
+ return test_thread.is_finish_called();
+ }
+ false
+ }
}
impl IInputFilterCallbacks for TestCallbacks {
@@ -371,5 +465,45 @@
self.inner().last_locked_modifier_state = locked_modifier_state as u32;
Result::Ok(())
}
+
+ fn createInputFilterThread(
+ &self,
+ _callback: &Strong<dyn IInputThreadCallback>,
+ ) -> std::result::Result<Strong<dyn IInputThread>, binder::Status> {
+ let test_thread = TestThread::new();
+ self.inner().test_thread = Some(test_thread.clone());
+ Result::Ok(BnInputThread::new_binder(test_thread, BinderFeatures::default()))
+ }
+ }
+
+ #[derive(Default)]
+ struct TestThreadInner {
+ is_finish_called: bool,
+ }
+
+ #[derive(Default, Clone)]
+ struct TestThread(Arc<RwLock<TestThreadInner>>);
+
+ impl Interface for TestThread {}
+
+ impl TestThread {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> {
+ self.0.write().unwrap()
+ }
+
+ pub fn is_finish_called(&self) -> bool {
+ self.0.read().unwrap().is_finish_called
+ }
+ }
+
+ impl IInputThread for TestThread {
+ fn finish(&self) -> binder::Result<()> {
+ self.inner().is_finish_called = true;
+ Result::Ok(())
+ }
}
}
diff --git a/services/inputflinger/rust/input_filter_thread.rs b/services/inputflinger/rust/input_filter_thread.rs
new file mode 100644
index 0000000..2d503ae
--- /dev/null
+++ b/services/inputflinger/rust/input_filter_thread.rs
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+//! Input filter thread implementation in rust.
+//! Using IInputFilter.aidl interface to create ever looping thread with JNI support, rest of
+//! thread handling is done from rust side.
+//!
+//! NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't
+//! have JNI support and can't call into Java policy that we use currently. libutils provided
+//! Thread.h also recommends against using std::thread and using the provided infrastructure that
+//! already provides way of attaching JniEnv to the created thread. So, we are using an AIDL
+//! interface to expose the InputThread infrastructure to rust.
+
+use crate::input_filter::InputFilterThreadCreator;
+use binder::{BinderFeatures, Interface, Strong};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputThread::{
+ IInputThread, IInputThreadCallback::BnInputThreadCallback,
+ IInputThreadCallback::IInputThreadCallback,
+};
+use log::{debug, error};
+use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+use std::sync::{Arc, RwLock, RwLockWriteGuard};
+use std::time::Duration;
+use std::{thread, thread::Thread};
+
+/// Interface to receive callback from Input filter thread
+pub trait ThreadCallback {
+ /// Calls back after the requested timeout expires.
+ /// {@see InputFilterThread.request_timeout_at_time(...)}
+ ///
+ /// NOTE: In case of multiple requests, the timeout request which is earliest in time, will be
+ /// fulfilled and notified to all the listeners. It's up to the listeners to re-request another
+ /// timeout in the future.
+ fn notify_timeout_expired(&self, when_nanos: i64);
+ /// Unique name for the listener, which will be used to uniquely identify the listener.
+ fn name(&self) -> &str;
+}
+
+#[derive(Clone)]
+pub struct InputFilterThread {
+ thread_creator: InputFilterThreadCreator,
+ thread_callback_handler: ThreadCallbackHandler,
+ inner: Arc<RwLock<InputFilterThreadInner>>,
+}
+
+struct InputFilterThreadInner {
+ cpp_thread: Option<Strong<dyn IInputThread>>,
+ looper: Option<Thread>,
+ next_timeout: i64,
+ is_finishing: bool,
+}
+
+impl InputFilterThread {
+ /// Create a new InputFilterThread instance.
+ /// NOTE: This will create a new thread. Clone the existing instance to reuse the same thread.
+ pub fn new(thread_creator: InputFilterThreadCreator) -> InputFilterThread {
+ Self {
+ thread_creator,
+ thread_callback_handler: ThreadCallbackHandler::new(),
+ inner: Arc::new(RwLock::new(InputFilterThreadInner {
+ cpp_thread: None,
+ looper: None,
+ next_timeout: i64::MAX,
+ is_finishing: false,
+ })),
+ }
+ }
+
+ /// Listener requesting a timeout in future will receive a callback at or before the requested
+ /// time on the input filter thread.
+ /// {@see ThreadCallback.notify_timeout_expired(...)}
+ pub fn request_timeout_at_time(&self, when_nanos: i64) {
+ let filter_thread = &mut self.filter_thread();
+ if when_nanos < filter_thread.next_timeout {
+ filter_thread.next_timeout = when_nanos;
+ if let Some(looper) = &filter_thread.looper {
+ looper.unpark();
+ }
+ }
+ }
+
+ /// Registers a callback listener.
+ ///
+ /// NOTE: If a listener with the same name already exists when registering using
+ /// {@see InputFilterThread.register_thread_callback(...)}, we will ignore the listener. You
+ /// must clear any previously registered listeners using
+ /// {@see InputFilterThread.unregister_thread_callback(...) before registering the new listener.
+ ///
+ /// NOTE: Also, registering a callback will start the looper if not already started.
+ pub fn register_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) {
+ self.thread_callback_handler.register_thread_callback(callback);
+ self.start();
+ }
+
+ /// Unregisters a callback listener.
+ ///
+ /// NOTE: Unregistering a callback will stop the looper if not other callback registered.
+ pub fn unregister_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) {
+ self.thread_callback_handler.unregister_thread_callback(callback);
+ // Stop the thread if no registered callbacks exist. We will recreate the thread when new
+ // callbacks are registered.
+ let has_callbacks = self.thread_callback_handler.has_callbacks();
+ if !has_callbacks {
+ self.stop();
+ }
+ }
+
+ fn start(&self) {
+ debug!("InputFilterThread: start thread");
+ let filter_thread = &mut self.filter_thread();
+ if filter_thread.cpp_thread.is_none() {
+ filter_thread.cpp_thread = Some(self.thread_creator.create(
+ &BnInputThreadCallback::new_binder(self.clone(), BinderFeatures::default()),
+ ));
+ filter_thread.looper = None;
+ filter_thread.is_finishing = false;
+ }
+ }
+
+ fn stop(&self) {
+ debug!("InputFilterThread: stop thread");
+ let filter_thread = &mut self.filter_thread();
+ filter_thread.is_finishing = true;
+ if let Some(looper) = &filter_thread.looper {
+ looper.unpark();
+ }
+ if let Some(cpp_thread) = &filter_thread.cpp_thread {
+ let _ = cpp_thread.finish();
+ }
+ // Clear all references
+ filter_thread.cpp_thread = None;
+ filter_thread.looper = None;
+ }
+
+ fn loop_once(&self, now: i64) {
+ let mut wake_up_time = i64::MAX;
+ let mut timeout_expired = false;
+ {
+ // acquire thread lock
+ let filter_thread = &mut self.filter_thread();
+ if filter_thread.is_finishing {
+ // Thread is finishing so don't block processing on it and let it loop.
+ return;
+ }
+ if filter_thread.next_timeout != i64::MAX {
+ if filter_thread.next_timeout <= now {
+ timeout_expired = true;
+ filter_thread.next_timeout = i64::MAX;
+ } else {
+ wake_up_time = filter_thread.next_timeout;
+ }
+ }
+ if filter_thread.looper.is_none() {
+ filter_thread.looper = Some(std::thread::current());
+ }
+ } // release thread lock
+ if timeout_expired {
+ self.thread_callback_handler.notify_timeout_expired(now);
+ }
+ if wake_up_time == i64::MAX {
+ thread::park();
+ } else {
+ let duration_now = Duration::from_nanos(now as u64);
+ let duration_wake_up = Duration::from_nanos(wake_up_time as u64);
+ thread::park_timeout(duration_wake_up - duration_now);
+ }
+ }
+
+ fn filter_thread(&self) -> RwLockWriteGuard<'_, InputFilterThreadInner> {
+ self.inner.write().unwrap()
+ }
+}
+
+impl Interface for InputFilterThread {}
+
+impl IInputThreadCallback for InputFilterThread {
+ fn loopOnce(&self) -> binder::Result<()> {
+ self.loop_once(clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds());
+ Result::Ok(())
+ }
+}
+
+#[derive(Default, Clone)]
+struct ThreadCallbackHandler(Arc<RwLock<ThreadCallbackHandlerInner>>);
+
+#[derive(Default)]
+struct ThreadCallbackHandlerInner {
+ callbacks: Vec<Box<dyn ThreadCallback + Send + Sync>>,
+}
+
+impl ThreadCallbackHandler {
+ fn new() -> Self {
+ Default::default()
+ }
+
+ fn has_callbacks(&self) -> bool {
+ !&self.0.read().unwrap().callbacks.is_empty()
+ }
+
+ fn register_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) {
+ let callbacks = &mut self.0.write().unwrap().callbacks;
+ if callbacks.iter().any(|x| x.name() == callback.name()) {
+ error!(
+ "InputFilterThread: register_thread_callback, callback {:?} already exists!",
+ callback.name()
+ );
+ return;
+ }
+ debug!(
+ "InputFilterThread: register_thread_callback, callback {:?} added!",
+ callback.name()
+ );
+ callbacks.push(callback);
+ }
+
+ fn unregister_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) {
+ let callbacks = &mut self.0.write().unwrap().callbacks;
+ if let Some(index) = callbacks.iter().position(|x| x.name() == callback.name()) {
+ callbacks.remove(index);
+ debug!(
+ "InputFilterThread: unregister_thread_callback, callback {:?} removed!",
+ callback.name()
+ );
+ return;
+ }
+ error!(
+ "InputFilterThread: unregister_thread_callback, callback {:?} doesn't exist",
+ callback.name()
+ );
+ }
+
+ fn notify_timeout_expired(&self, when_nanos: i64) {
+ let callbacks = &self.0.read().unwrap().callbacks;
+ for callback in callbacks.iter() {
+ callback.notify_timeout_expired(when_nanos);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::test_callbacks::TestCallbacks;
+ use crate::input_filter_thread::{
+ test_thread::TestThread, test_thread_callback::TestThreadCallback,
+ };
+
+ #[test]
+ fn test_register_callback_creates_cpp_thread() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread_callback = TestThreadCallback::new();
+ test_thread.register_thread_callback(test_thread_callback);
+ assert!(test_callbacks.is_thread_created());
+ }
+
+ #[test]
+ fn test_unregister_callback_finishes_cpp_thread() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread_callback = TestThreadCallback::new();
+ test_thread.register_thread_callback(test_thread_callback.clone());
+ test_thread.unregister_thread_callback(test_thread_callback);
+ assert!(test_callbacks.is_thread_finished());
+ }
+
+ #[test]
+ fn test_notify_timeout_called_after_timeout_expired() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread_callback = TestThreadCallback::new();
+ test_thread.register_thread_callback(test_thread_callback.clone());
+ test_thread.start_looper();
+
+ test_thread.request_timeout_at_time(500);
+ test_thread.dispatch_next();
+
+ test_thread.move_time_forward(500);
+
+ test_thread.stop_looper();
+ assert!(test_thread_callback.is_notify_timeout_called());
+ }
+
+ #[test]
+ fn test_notify_timeout_not_called_before_timeout_expired() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread_callback = TestThreadCallback::new();
+ test_thread.register_thread_callback(test_thread_callback.clone());
+ test_thread.start_looper();
+
+ test_thread.request_timeout_at_time(500);
+ test_thread.dispatch_next();
+
+ test_thread.move_time_forward(100);
+
+ test_thread.stop_looper();
+ assert!(!test_thread_callback.is_notify_timeout_called());
+ }
+}
+
+#[cfg(test)]
+pub mod test_thread {
+
+ use crate::input_filter::{test_callbacks::TestCallbacks, InputFilterThreadCreator};
+ use crate::input_filter_thread::{test_thread_callback::TestThreadCallback, InputFilterThread};
+ use binder::Strong;
+ use std::sync::{
+ atomic::AtomicBool, atomic::AtomicI64, atomic::Ordering, Arc, RwLock, RwLockWriteGuard,
+ };
+ use std::time::Duration;
+
+ #[derive(Clone)]
+ pub struct TestThread {
+ input_thread: InputFilterThread,
+ inner: Arc<RwLock<TestThreadInner>>,
+ exit_flag: Arc<AtomicBool>,
+ now: Arc<AtomicI64>,
+ }
+
+ struct TestThreadInner {
+ join_handle: Option<std::thread::JoinHandle<()>>,
+ }
+
+ impl TestThread {
+ pub fn new(callbacks: TestCallbacks) -> TestThread {
+ Self {
+ input_thread: InputFilterThread::new(InputFilterThreadCreator::new(Arc::new(
+ RwLock::new(Strong::new(Box::new(callbacks))),
+ ))),
+ inner: Arc::new(RwLock::new(TestThreadInner { join_handle: None })),
+ exit_flag: Arc::new(AtomicBool::new(false)),
+ now: Arc::new(AtomicI64::new(0)),
+ }
+ }
+
+ fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> {
+ self.inner.write().unwrap()
+ }
+
+ pub fn get_input_thread(&self) -> InputFilterThread {
+ self.input_thread.clone()
+ }
+
+ pub fn register_thread_callback(&self, thread_callback: TestThreadCallback) {
+ self.input_thread.register_thread_callback(Box::new(thread_callback));
+ }
+
+ pub fn unregister_thread_callback(&self, thread_callback: TestThreadCallback) {
+ self.input_thread.unregister_thread_callback(Box::new(thread_callback));
+ }
+
+ pub fn start_looper(&self) {
+ self.exit_flag.store(false, Ordering::Relaxed);
+ let clone = self.clone();
+ let join_handle = std::thread::Builder::new()
+ .name("test_thread".to_string())
+ .spawn(move || {
+ while !clone.exit_flag.load(Ordering::Relaxed) {
+ clone.loop_once();
+ }
+ })
+ .unwrap();
+ self.inner().join_handle = Some(join_handle);
+ // Sleep until the looper thread starts
+ std::thread::sleep(Duration::from_millis(10));
+ }
+
+ pub fn stop_looper(&self) {
+ self.exit_flag.store(true, Ordering::Relaxed);
+ {
+ let mut inner = self.inner();
+ if let Some(join_handle) = &inner.join_handle {
+ join_handle.thread().unpark();
+ }
+ inner.join_handle.take().map(std::thread::JoinHandle::join);
+ inner.join_handle = None;
+ }
+ self.exit_flag.store(false, Ordering::Relaxed);
+ }
+
+ pub fn move_time_forward(&self, value: i64) {
+ let _ = self.now.fetch_add(value, Ordering::Relaxed);
+ self.dispatch_next();
+ }
+
+ pub fn dispatch_next(&self) {
+ if let Some(join_handle) = &self.inner().join_handle {
+ join_handle.thread().unpark();
+ }
+ // Sleep until the looper thread runs a loop
+ std::thread::sleep(Duration::from_millis(10));
+ }
+
+ fn loop_once(&self) {
+ self.input_thread.loop_once(self.now.load(Ordering::Relaxed));
+ }
+
+ pub fn request_timeout_at_time(&self, when_nanos: i64) {
+ self.input_thread.request_timeout_at_time(when_nanos);
+ }
+ }
+}
+
+#[cfg(test)]
+pub mod test_thread_callback {
+ use crate::input_filter_thread::ThreadCallback;
+ use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+ #[derive(Default)]
+ struct TestThreadCallbackInner {
+ is_notify_timeout_called: bool,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestThreadCallback(Arc<RwLock<TestThreadCallbackInner>>);
+
+ impl TestThreadCallback {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&self) -> RwLockWriteGuard<'_, TestThreadCallbackInner> {
+ self.0.write().unwrap()
+ }
+
+ pub fn is_notify_timeout_called(&self) -> bool {
+ self.0.read().unwrap().is_notify_timeout_called
+ }
+ }
+
+ impl ThreadCallback for TestThreadCallback {
+ fn notify_timeout_expired(&self, _when_nanos: i64) {
+ self.inner().is_notify_timeout_called = true;
+ }
+ fn name(&self) -> &str {
+ "TestThreadCallback"
+ }
+ }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index fa16898..da72134 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -21,6 +21,8 @@
mod bounce_keys_filter;
mod input_filter;
+mod input_filter_thread;
+mod slow_keys_filter;
mod sticky_keys_filter;
use crate::input_filter::InputFilter;
diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
new file mode 100644
index 0000000..01165b5
--- /dev/null
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -0,0 +1,382 @@
+/*
+ * 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.
+ */
+
+//! Slow keys input filter implementation.
+//! Slow keys is an accessibility feature to aid users who have physical disabilities, that allows
+//! the user to specify the duration for which one must press-and-hold a key before the system
+//! accepts the keypress.
+use crate::input_filter::Filter;
+use crate::input_filter_thread::{InputFilterThread, ThreadCallback};
+use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use log::debug;
+use std::collections::HashSet;
+use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
+
+#[derive(Debug)]
+struct OngoingKeyDown {
+ scancode: i32,
+ device_id: i32,
+ down_time: i64,
+}
+
+struct SlowKeysFilterInner {
+ next: Box<dyn Filter + Send + Sync>,
+ slow_key_threshold_ns: i64,
+ external_devices: HashSet<i32>,
+ // This tracks KeyEvents that are blocked by Slow keys filter and will be passed through if the
+ // press duration exceeds the slow keys threshold.
+ pending_down_events: Vec<KeyEvent>,
+ // This tracks KeyEvent streams that have press duration greater than the slow keys threshold,
+ // hence any future ACTION_DOWN (if repeats are handled on HW side) or ACTION_UP are allowed to
+ // pass through without waiting.
+ ongoing_down_events: Vec<OngoingKeyDown>,
+ input_filter_thread: InputFilterThread,
+}
+
+#[derive(Clone)]
+pub struct SlowKeysFilter(Arc<RwLock<SlowKeysFilterInner>>);
+
+impl SlowKeysFilter {
+ /// Create a new SlowKeysFilter instance.
+ pub fn new(
+ next: Box<dyn Filter + Send + Sync>,
+ slow_key_threshold_ns: i64,
+ input_filter_thread: InputFilterThread,
+ ) -> SlowKeysFilter {
+ let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner {
+ next,
+ slow_key_threshold_ns,
+ external_devices: HashSet::new(),
+ pending_down_events: Vec::new(),
+ ongoing_down_events: Vec::new(),
+ input_filter_thread: input_filter_thread.clone(),
+ })));
+ input_filter_thread.register_thread_callback(Box::new(filter.clone()));
+ filter
+ }
+
+ fn read_inner(&self) -> RwLockReadGuard<'_, SlowKeysFilterInner> {
+ self.0.read().unwrap()
+ }
+
+ fn write_inner(&self) -> RwLockWriteGuard<'_, SlowKeysFilterInner> {
+ self.0.write().unwrap()
+ }
+
+ fn request_next_callback(&self) {
+ let slow_filter = &self.read_inner();
+ if slow_filter.pending_down_events.is_empty() {
+ return;
+ }
+ if let Some(event) = slow_filter.pending_down_events.iter().min_by_key(|x| x.downTime) {
+ slow_filter.input_filter_thread.request_timeout_at_time(event.downTime);
+ }
+ }
+}
+
+impl Filter for SlowKeysFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ {
+ // acquire write lock
+ let mut slow_filter = self.write_inner();
+ if !(slow_filter.external_devices.contains(&event.deviceId)
+ && event.source == Source::KEYBOARD)
+ {
+ slow_filter.next.notify_key(event);
+ return;
+ }
+ // Pass all events through if key down has already been processed
+ // Do update the downtime before sending the events through
+ if let Some(index) = slow_filter
+ .ongoing_down_events
+ .iter()
+ .position(|x| x.device_id == event.deviceId && x.scancode == event.scanCode)
+ {
+ let mut new_event = *event;
+ new_event.downTime = slow_filter.ongoing_down_events[index].down_time;
+ slow_filter.next.notify_key(&new_event);
+ if event.action == KeyEventAction::UP {
+ slow_filter.ongoing_down_events.remove(index);
+ }
+ return;
+ }
+ match event.action {
+ KeyEventAction::DOWN => {
+ if slow_filter
+ .pending_down_events
+ .iter()
+ .any(|x| x.deviceId == event.deviceId && x.scanCode == event.scanCode)
+ {
+ debug!("Dropping key down event since another pending down event exists");
+ return;
+ }
+ let mut pending_event = *event;
+ pending_event.downTime += slow_filter.slow_key_threshold_ns;
+ pending_event.eventTime = pending_event.downTime;
+ slow_filter.pending_down_events.push(pending_event);
+ }
+ KeyEventAction::UP => {
+ debug!("Dropping key up event due to insufficient press duration");
+ if let Some(index) = slow_filter
+ .pending_down_events
+ .iter()
+ .position(|x| x.deviceId == event.deviceId && x.scanCode == event.scanCode)
+ {
+ slow_filter.pending_down_events.remove(index);
+ }
+ }
+ _ => (),
+ }
+ } // release write lock
+ self.request_next_callback();
+ }
+
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+ let mut slow_filter = self.write_inner();
+ slow_filter
+ .pending_down_events
+ .retain(|event| device_infos.iter().any(|x| event.deviceId == x.deviceId));
+ slow_filter
+ .ongoing_down_events
+ .retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId));
+ slow_filter.external_devices.clear();
+ for device_info in device_infos {
+ if device_info.external {
+ slow_filter.external_devices.insert(device_info.deviceId);
+ }
+ }
+ slow_filter.next.notify_devices_changed(device_infos);
+ }
+
+ fn destroy(&mut self) {
+ let mut slow_filter = self.write_inner();
+ slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone()));
+ slow_filter.next.destroy();
+ }
+}
+
+impl ThreadCallback for SlowKeysFilter {
+ fn notify_timeout_expired(&self, when_nanos: i64) {
+ {
+ // acquire write lock
+ let slow_filter = &mut self.write_inner();
+ for event in slow_filter.pending_down_events.clone() {
+ if event.downTime <= when_nanos {
+ slow_filter.next.notify_key(&event);
+ slow_filter.ongoing_down_events.push(OngoingKeyDown {
+ scancode: event.scanCode,
+ device_id: event.deviceId,
+ down_time: event.downTime,
+ });
+ }
+ }
+ slow_filter.pending_down_events.retain(|event| event.downTime > when_nanos);
+ } // release write lock
+ self.request_next_callback();
+ }
+
+ fn name(&self) -> &str {
+ "slow_keys_filter"
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::{test_callbacks::TestCallbacks, test_filter::TestFilter, Filter};
+ use crate::input_filter_thread::test_thread::TestThread;
+ use crate::slow_keys_filter::SlowKeysFilter;
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+ };
+
+ static BASE_KEY_EVENT: KeyEvent = KeyEvent {
+ id: 1,
+ deviceId: 1,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 1,
+ scanCode: 0,
+ metaState: 0,
+ };
+
+ #[test]
+ fn test_is_notify_key_for_internal_keyboard_not_blocked() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_internal_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+ test_thread.start_looper();
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_for_external_stylus_not_blocked() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+ test_thread.start_looper();
+
+ let event =
+ KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+ test_thread.start_looper();
+
+ filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
+ assert!(next.last_event().is_none());
+ test_thread.dispatch_next();
+
+ test_thread.move_time_forward(100);
+
+ test_thread.stop_looper();
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: 100,
+ eventTime: 100,
+ ..BASE_KEY_EVENT
+ }
+ );
+ }
+
+ #[test]
+ fn test_notify_key_for_external_keyboard_when_key_not_pressed_for_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+ test_thread.start_looper();
+
+ filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
+ test_thread.dispatch_next();
+
+ test_thread.move_time_forward(10);
+
+ filter.notify_key(&KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT });
+ test_thread.dispatch_next();
+
+ test_thread.stop_looper();
+ assert!(next.last_event().is_none());
+ }
+
+ #[test]
+ fn test_notify_key_for_external_keyboard_when_device_removed_before_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = TestThread::new(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+ test_thread.start_looper();
+
+ filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
+ assert!(next.last_event().is_none());
+ test_thread.dispatch_next();
+
+ filter.notify_devices_changed(&[]);
+ test_thread.dispatch_next();
+
+ test_thread.move_time_forward(100);
+
+ test_thread.stop_looper();
+ assert!(next.last_event().is_none());
+ }
+
+ fn setup_filter_with_external_device(
+ next: Box<dyn Filter + Send + Sync>,
+ test_thread: TestThread,
+ device_id: i32,
+ threshold: i64,
+ ) -> SlowKeysFilter {
+ setup_filter_with_devices(
+ next,
+ test_thread,
+ &[DeviceInfo { deviceId: device_id, external: true }],
+ threshold,
+ )
+ }
+
+ fn setup_filter_with_internal_device(
+ next: Box<dyn Filter + Send + Sync>,
+ test_thread: TestThread,
+ device_id: i32,
+ threshold: i64,
+ ) -> SlowKeysFilter {
+ setup_filter_with_devices(
+ next,
+ test_thread,
+ &[DeviceInfo { deviceId: device_id, external: false }],
+ threshold,
+ )
+ }
+
+ fn setup_filter_with_devices(
+ next: Box<dyn Filter + Send + Sync>,
+ test_thread: TestThread,
+ devices: &[DeviceInfo],
+ threshold: i64,
+ ) -> SlowKeysFilter {
+ let mut filter = SlowKeysFilter::new(next, threshold, test_thread.get_input_thread());
+ filter.notify_devices_changed(devices);
+ filter
+ }
+}
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index da581b8..6c2277c 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -142,6 +142,10 @@
}
self.next.notify_devices_changed(device_infos);
}
+
+ fn destroy(&mut self) {
+ self.next.destroy();
+ }
}
fn is_modifier_key(keycode: i32) -> bool {
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index e104543..7b793d8 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -1324,6 +1324,42 @@
ASSERT_EQ(20, relY2);
}
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAssociatedViewport) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
+ mReaderConfiguration.setDisplayViewports({primaryViewport});
+ createDevice();
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+
+ // Verify that acceleration is being applied by default by checking that the movement is scaled.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID)))));
+ const auto& coords = get<NotifyMotionArgs>(args.back()).pointerCoords[0];
+ ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), 10.f);
+ ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f);
+
+ // Disable acceleration for the display, and verify that acceleration is no longer applied.
+ mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
+ args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::POINTER_SPEED);
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID),
+ WithRelativeMotion(10, 20)))));
+}
+
namespace {
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 48d7e55..f1f4a61 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -156,6 +156,20 @@
sp<IBinder> token{};
gui::Pid pid{gui::Pid::INVALID};
};
+ /* Stores data about a user-activity-poke event from the dispatcher. */
+ struct UserActivityPokeEvent {
+ nsecs_t eventTime;
+ int32_t eventType;
+ int32_t displayId;
+
+ bool operator==(const UserActivityPokeEvent& rhs) const = default;
+
+ friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) {
+ os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType
+ << ", displayId=" << ev.displayId << "]";
+ return os;
+ }
+ };
public:
FakeInputDispatcherPolicy() = default;
@@ -351,14 +365,36 @@
void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; }
- void assertUserActivityPoked() {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked";
+ void assertUserActivityNotPoked() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::optional<UserActivityPokeEvent> pokeEvent =
+ getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock,
+ mNotifyUserActivity);
+
+ ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked";
}
- void assertUserActivityNotPoked() {
- std::scoped_lock lock(mLock);
- ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked";
+ /**
+ * Asserts that a user activity poke has happened. The earliest recorded poke event will be
+ * cleared after this call.
+ *
+ * If an expected UserActivityPokeEvent is provided, asserts that the given event is the
+ * earliest recorded poke event.
+ */
+ void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {}) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::optional<UserActivityPokeEvent> pokeEvent =
+ getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock,
+ mNotifyUserActivity);
+ ASSERT_TRUE(pokeEvent) << "Expected a user poke event";
+
+ if (expectedPokeEvent) {
+ ASSERT_EQ(expectedPokeEvent, *pokeEvent);
+ }
}
void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) {
@@ -414,7 +450,9 @@
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
- bool mPokedUserActivity GUARDED_BY(mLock) = false;
+
+ std::condition_variable mNotifyUserActivity;
+ std::queue<UserActivityPokeEvent> mUserActivityPokeEvents;
std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
@@ -576,9 +614,10 @@
NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask);
}
- void pokeUserActivity(nsecs_t, int32_t, int32_t) override {
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override {
std::scoped_lock lock(mLock);
- mPokedUserActivity = true;
+ mNotifyUserActivity.notify_all();
+ mUserActivityPokeEvents.push({eventTime, eventType, displayId});
}
bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override {
@@ -861,30 +900,31 @@
explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
: mConsumer(std::move(clientChannel)), mName(name) {}
- InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) {
- InputEvent* event;
- std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
+ std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = false) {
+ auto [consumeSeq, event] = receiveEvent(timeout);
if (!consumeSeq) {
return nullptr;
}
finishEvent(*consumeSeq, handled);
- return event;
+ return std::move(event);
}
/**
* Receive an event without acknowledging it.
* Return the sequence number that could later be used to send finished signal.
*/
- std::optional<uint32_t> receiveEvent(std::chrono::milliseconds timeout,
- InputEvent** outEvent = nullptr) {
+ std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent(
+ std::chrono::milliseconds timeout) {
uint32_t consumeSeq;
- InputEvent* event;
+ std::unique_ptr<InputEvent> event;
std::chrono::time_point start = std::chrono::steady_clock::now();
status_t status = WOULD_BLOCK;
while (status == WOULD_BLOCK) {
+ InputEvent* rawEventPtr = nullptr;
status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &event);
+ &rawEventPtr);
+ event = std::unique_ptr<InputEvent>(rawEventPtr);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > timeout) {
break;
@@ -893,21 +933,17 @@
if (status == WOULD_BLOCK) {
// Just means there's no event available.
- return std::nullopt;
+ return std::make_pair(std::nullopt, nullptr);
}
if (status != OK) {
ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
- return std::nullopt;
+ return std::make_pair(std::nullopt, nullptr);
}
if (event == nullptr) {
ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
- return std::nullopt;
}
- if (outEvent != nullptr) {
- *outEvent = event;
- }
- return consumeSeq;
+ return std::make_pair(consumeSeq, std::move(event));
}
/**
@@ -926,7 +962,7 @@
void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
@@ -970,8 +1006,8 @@
}
}
- MotionEvent* consumeMotion() {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ std::unique_ptr<MotionEvent> consumeMotion() {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
@@ -982,17 +1018,17 @@
ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event;
return nullptr;
}
- return static_cast<MotionEvent*>(event);
+ return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
}
void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
- MotionEvent* motionEvent = consumeMotion();
+ std::unique_ptr<MotionEvent> motionEvent = consumeMotion();
ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
ASSERT_THAT(*motionEvent, matcher);
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::FOCUS, event->getType())
@@ -1001,12 +1037,12 @@
ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
<< mName.c_str() << ": event displayId should always be NONE.";
- FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
- EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ EXPECT_EQ(hasFocus, focusEvent.getHasFocus());
}
void consumeCaptureEvent(bool hasCapture) {
- const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::CAPTURE, event->getType())
@@ -1020,7 +1056,7 @@
}
void consumeDragEvent(bool isExiting, float x, float y) {
- const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
@@ -1035,7 +1071,7 @@
}
void consumeTouchModeEvent(bool inTouchMode) {
- const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
@@ -1048,7 +1084,7 @@
}
void assertNoEvents() {
- InputEvent* event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
if (event == nullptr) {
return;
}
@@ -1081,7 +1117,7 @@
private:
InputConsumer mConsumer;
- PreallocatedInputEventFactory mEventFactory;
+ DynamicInputEventFactory mEventFactory;
std::string mName;
};
@@ -1229,17 +1265,23 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
- const KeyEvent& consumeKey(bool handled = true) {
- const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
- if (event.getType() != InputEventType::KEY) {
- LOG(FATAL) << "Instead of key event, got " << event;
+ std::unique_ptr<KeyEvent> consumeKey(bool handled = true) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
+ if (event == nullptr) {
+ ADD_FAILURE() << "No event";
+ return nullptr;
}
- return static_cast<const KeyEvent&>(event);
+ if (event->getType() != InputEventType::KEY) {
+ ADD_FAILURE() << "Instead of key event, got " << event;
+ return nullptr;
+ }
+ return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release()));
}
void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
- const KeyEvent& keyEvent = consumeKey();
- ASSERT_THAT(keyEvent, matcher);
+ std::unique_ptr<KeyEvent> keyEvent = consumeKey();
+ ASSERT_NE(nullptr, keyEvent);
+ ASSERT_THAT(*keyEvent, matcher);
}
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
@@ -1325,14 +1367,20 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
- const MotionEvent& consumeMotionEvent(
+ std::unique_ptr<MotionEvent> consumeMotionEvent(
const ::testing::Matcher<MotionEvent>& matcher = testing::_) {
- const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- if (event.getType() != InputEventType::MOTION) {
- LOG(FATAL) << "Instead of motion event, got " << event;
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event == nullptr) {
+ ADD_FAILURE() << "No event";
+ return nullptr;
}
- const auto& motionEvent = static_cast<const MotionEvent&>(event);
- EXPECT_THAT(motionEvent, matcher);
+ if (event->getType() != InputEventType::MOTION) {
+ ADD_FAILURE() << "Instead of motion event, got " << *event;
+ return nullptr;
+ }
+ std::unique_ptr<MotionEvent> motionEvent =
+ std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
+ EXPECT_THAT(*motionEvent, matcher);
return motionEvent;
}
@@ -1346,12 +1394,12 @@
mInputReceiver->consumeTouchModeEvent(inTouchMode);
}
- std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
+ std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
- return std::nullopt;
+ return std::make_pair(std::nullopt, nullptr);
}
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED, outEvent);
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
}
void finishEvent(uint32_t sequenceNum) {
@@ -1396,15 +1444,15 @@
static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
friend class sp<FakeWindowHandle>;
- const InputEvent& consume(std::chrono::milliseconds timeout, bool handled = true) {
+ std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) {
if (mInputReceiver == nullptr) {
LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
}
- InputEvent* event = mInputReceiver->consume(timeout, handled);
+ std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled);
if (event == nullptr) {
- LOG(FATAL) << "Consume failed: no event";
+ ADD_FAILURE() << "Consume failed: no event";
}
- return *event;
+ return event;
}
};
@@ -1423,7 +1471,8 @@
}
std::optional<int32_t> receiveEvent() {
- return mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ const auto [sequenceNum, _] = mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ return sequenceNum;
}
void finishEvent(uint32_t consumeSeq) { return mInputReceiver.finishEvent(consumeSeq); }
@@ -1461,7 +1510,7 @@
mInputReceiver.consumeMotionEvent(matcher);
}
- MotionEvent* consumeMotion() { return mInputReceiver.consumeMotion(); }
+ std::unique_ptr<MotionEvent> consumeMotion() { return mInputReceiver.consumeMotion(); }
void assertNoEvents() { mInputReceiver.assertNoEvents(); }
@@ -3779,18 +3828,20 @@
mDispatcher->waitForIdle();
- const MotionEvent& motionEvent1 = window1->consumeMotionEvent();
+ std::unique_ptr<MotionEvent> motionEvent1 = window1->consumeMotionEvent();
+ ASSERT_NE(nullptr, motionEvent1);
window2->assertNoEvents();
- nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
- ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
+ nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
+ ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- const MotionEvent& motionEvent2 = window2->consumeMotionEvent();
- nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
+ std::unique_ptr<MotionEvent> motionEvent2 = window2->consumeMotionEvent();
+ ASSERT_NE(nullptr, motionEvent2);
+ nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
+ ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
@@ -4624,12 +4675,13 @@
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- const MotionEvent& event = window->consumeMotionEvent();
- EXPECT_EQ(POINTER_1_DOWN, event.getAction());
- EXPECT_EQ(70, event.getX(0)); // 50 + 20
- EXPECT_EQ(90, event.getY(0)); // 50 + 40
- EXPECT_EQ(-10, event.getX(1)); // -30 + 20
- EXPECT_EQ(-10, event.getY(1)); // -50 + 40
+ std::unique_ptr<MotionEvent> event = window->consumeMotionEvent();
+ ASSERT_NE(nullptr, event);
+ EXPECT_EQ(POINTER_1_DOWN, event->getAction());
+ EXPECT_EQ(70, event->getX(0)); // 50 + 20
+ EXPECT_EQ(90, event->getY(0)); // 50 + 40
+ EXPECT_EQ(-10, event->getX(1)); // -30 + 20
+ EXPECT_EQ(-10, event->getY(1)); // -50 + 40
}
/**
@@ -4895,15 +4947,16 @@
EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindowDefaultDisplay->getToken()));
// windowDefaultDisplay gets cancel
- const MotionEvent& event = windowDefaultDisplay->consumeMotionEvent();
- EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
+ std::unique_ptr<MotionEvent> event = windowDefaultDisplay->consumeMotionEvent();
+ ASSERT_NE(nullptr, event);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event->getAction());
// The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so the
// coordinates of the cancel are converted by windowDefaultDisplay's transform, the x and y
// coordinates are both 100, otherwise if the cancel event is sent to windowSecondDisplay of
// SECOND_DISPLAY_ID, the x and y coordinates are 200
- EXPECT_EQ(100, event.getX(0));
- EXPECT_EQ(100, event.getY(0));
+ EXPECT_EQ(100, event->getX(0));
+ EXPECT_EQ(100, event->getY(0));
}
/**
@@ -5033,18 +5086,19 @@
{PointF{150, 220}}));
firstWindow->assertNoEvents();
- const MotionEvent& event = secondWindow->consumeMotionEvent();
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event.getAction());
+ std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
+ ASSERT_NE(nullptr, event);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
// Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event.getRawX(0));
- EXPECT_EQ(880, event.getRawY(0));
+ EXPECT_EQ(300, event->getRawX(0));
+ EXPECT_EQ(880, event->getRawY(0));
// Ensure that the x and y values are in the window's coordinate space.
// The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
// the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event.getX(0));
- EXPECT_EQ(80, event.getY(0));
+ EXPECT_EQ(100, event->getX(0));
+ EXPECT_EQ(80, event->getY(0));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
@@ -6081,7 +6135,8 @@
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- MotionEvent* event = monitor.consumeMotion();
+ std::unique_ptr<MotionEvent> event = monitor.consumeMotion();
+ ASSERT_NE(nullptr, event);
// Even though window has transform, gesture monitor must not.
ASSERT_EQ(ui::Transform(), event->getTransform());
}
@@ -6188,9 +6243,9 @@
const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
mDispatcher->notifyKey(keyArgs);
- const KeyEvent& event = window->consumeKey();
-
- std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event);
+ std::unique_ptr<KeyEvent> event = window->consumeKey();
+ ASSERT_NE(event, nullptr);
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
ASSERT_NE(verified, nullptr);
ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY);
@@ -6231,9 +6286,9 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(motionArgs);
- const MotionEvent& event = window->consumeMotionEvent();
-
- std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event);
+ std::unique_ptr<MotionEvent> event = window->consumeMotionEvent();
+ ASSERT_NE(nullptr, event);
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
ASSERT_NE(verified, nullptr);
ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION);
@@ -6760,13 +6815,13 @@
ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
mDispatcher->notifyMotion(notifyArgs);
- const MotionEvent& leftEnter = left->consumeMotionEvent(
+ std::unique_ptr<MotionEvent> leftEnter = left->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_ENTER), Not(WithEventId(notifyArgs.id)),
WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
-
+ ASSERT_NE(nullptr, leftEnter);
spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
Not(WithEventId(notifyArgs.id)),
- Not(WithEventId(leftEnter.getId())),
+ Not(WithEventId(leftEnter->getId())),
WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
// Send move to the right window, and ensure hover exit and enter are synthesized with new ids.
@@ -6774,13 +6829,13 @@
{PointF{150, 50}});
mDispatcher->notifyMotion(notifyArgs);
- const MotionEvent& leftExit = left->consumeMotionEvent(
+ std::unique_ptr<MotionEvent> leftExit = left->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_EXIT), Not(WithEventId(notifyArgs.id)),
WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
-
+ ASSERT_NE(nullptr, leftExit);
right->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
Not(WithEventId(notifyArgs.id)),
- Not(WithEventId(leftExit.getId())),
+ Not(WithEventId(leftExit->getId())),
WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithEventId(notifyArgs.id)));
@@ -6811,8 +6866,9 @@
}
void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) {
- const KeyEvent& event = mWindow->consumeKey(handled);
- ASSERT_THAT(event, matcher);
+ std::unique_ptr<KeyEvent> event = mWindow->consumeKey(handled);
+ ASSERT_NE(nullptr, event);
+ ASSERT_THAT(*event, matcher);
}
};
@@ -7146,9 +7202,10 @@
GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- const KeyEvent& repeatEvent = mWindow->consumeKey();
+ std::unique_ptr<KeyEvent> repeatEvent = mWindow->consumeKey();
+ ASSERT_NE(nullptr, repeatEvent);
EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
- IdGenerator::getSource(repeatEvent.getId()));
+ IdGenerator::getSource(repeatEvent->getId()));
}
}
@@ -7158,8 +7215,9 @@
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- const KeyEvent& repeatEvent = mWindow->consumeKey();
- int32_t id = repeatEvent.getId();
+ std::unique_ptr<KeyEvent> repeatEvent = mWindow->consumeKey();
+ ASSERT_NE(nullptr, repeatEvent);
+ int32_t id = repeatEvent->getId();
EXPECT_EQ(idSet.end(), idSet.find(id));
idSet.insert(id);
}
@@ -7671,6 +7729,130 @@
/*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0);
}
+class InputDispatcherUserActivityPokeTests : public InputDispatcherTest {
+protected:
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+ application->setDispatchingTimeout(100ms);
+ mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow",
+ ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+ mWindow->setDispatchingTimeout(100ms);
+ mWindow->setFocusable(true);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
+ setFocusedWindow(mWindow);
+ mWindow->consumeFocusEvent(true);
+ }
+
+ void notifyAndConsumeMotion(int32_t action, uint32_t source, int32_t displayId,
+ nsecs_t eventTime) {
+ mDispatcher->notifyMotion(MotionArgsBuilder(action, source)
+ .displayId(displayId)
+ .eventTime(eventTime)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(WithMotionAction(action));
+ }
+
+private:
+ sp<FakeWindowHandle> mWindow;
+};
+
+TEST_F_WITH_FLAGS(
+ InputDispatcherUserActivityPokeTests, MinPokeTimeObserved,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rate_limit_user_activity_poke_in_dispatcher))) {
+ mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
+
+ // First event of type TOUCH. Should poke.
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(50));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+
+ // 80ns > 50ns has passed since previous TOUCH event. Should poke.
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(130));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+
+ // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event).
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(135));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}});
+
+ // Within 50ns of previous TOUCH event. Should NOT poke.
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(140));
+ mFakePolicy->assertUserActivityNotPoked();
+
+ // Within 50ns of previous OTHER event. Should NOT poke.
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(150));
+ mFakePolicy->assertUserActivityNotPoked();
+
+ // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke.
+ // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source.
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(160));
+ mFakePolicy->assertUserActivityNotPoked();
+
+ // 65ns > 50ns has passed since previous OTHER event. Should poke.
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(200));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}});
+
+ // 170ns > 50ns has passed since previous TOUCH event. Should poke.
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(300));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+
+ // Assert that there's no more user activity poke event.
+ mFakePolicy->assertUserActivityNotPoked();
+}
+
+TEST_F_WITH_FLAGS(
+ InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rate_limit_user_activity_poke_in_dispatcher))) {
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(200));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(280));
+ mFakePolicy->assertUserActivityNotPoked();
+
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ milliseconds_to_nanoseconds(340));
+ mFakePolicy->assertUserActivityPoked(
+ {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+}
+
+TEST_F_WITH_FLAGS(
+ InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rate_limit_user_activity_poke_in_dispatcher))) {
+ mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
+
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20);
+ mFakePolicy->assertUserActivityPoked();
+
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 30);
+ mFakePolicy->assertUserActivityPoked();
+}
+
class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
@@ -7813,21 +7995,21 @@
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
const std::string name = window->getName();
- const MotionEvent& motionEvent =
+ std::unique_ptr<MotionEvent> motionEvent =
window->consumeMotionEvent(WithMotionAction(expectedAction));
-
- ASSERT_EQ(points.size(), motionEvent.getPointerCount());
+ ASSERT_NE(nullptr, motionEvent);
+ ASSERT_EQ(points.size(), motionEvent->getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
float expectedY = points[i].y;
- EXPECT_EQ(expectedX, motionEvent.getX(i))
+ EXPECT_EQ(expectedX, motionEvent->getX(i))
<< "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getX(i);
- EXPECT_EQ(expectedY, motionEvent.getY(i))
+ << ", got " << motionEvent->getX(i);
+ EXPECT_EQ(expectedY, motionEvent->getY(i))
<< "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getY(i);
+ << ", got " << motionEvent->getY(i);
}
}
@@ -8115,7 +8297,7 @@
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
- std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
+ const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
@@ -8131,7 +8313,7 @@
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
- std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
+ const auto [sequenceNum, _] = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
@@ -8291,7 +8473,7 @@
WINDOW_LOCATION));
mWindow->consumeMotionDown();
- std::optional<uint32_t> sequenceNum = spy->receiveEvent(); // ACTION_DOWN
+ const auto [sequenceNum, _] = spy->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = spy->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy);
@@ -8446,9 +8628,9 @@
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
tapOnWindow();
- std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ const auto& [downSequenceNum, downEvent] = mWindow->receiveEvent();
ASSERT_TRUE(downSequenceNum);
- std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ const auto& [upSequenceNum, upEvent] = mWindow->receiveEvent();
ASSERT_TRUE(upSequenceNum);
// Don't finish the events yet, and send a key
// Injection will "succeed" because we will eventually give up and send the key to the focused
@@ -8486,9 +8668,9 @@
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
tapOnWindow();
- std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ const auto& [downSequenceNum, _] = mWindow->receiveEvent();
ASSERT_TRUE(downSequenceNum);
- std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ const auto& [upSequenceNum, upEvent] = mWindow->receiveEvent();
ASSERT_TRUE(upSequenceNum);
// Don't finish the events yet, and send a key
mDispatcher->notifyKey(
@@ -8524,7 +8706,7 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
.build());
- std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
+ const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
@@ -8653,7 +8835,7 @@
.x(FOCUSED_WINDOW_LOCATION.x)
.y(FOCUSED_WINDOW_LOCATION.y))
.build()));
- std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
+ const auto [unfocusedSequenceNum, _] = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
const std::chrono::duration timeout =
@@ -8718,9 +8900,9 @@
tapOnFocusedWindow();
mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// Receive the events, but don't respond
- std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
+ const auto [downEventSequenceNum, downEvent] = mFocusedWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(downEventSequenceNum);
- std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP
+ const auto [upEventSequenceNum, upEvent] = mFocusedWindow->receiveEvent(); // ACTION_UP
ASSERT_TRUE(upEventSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -8798,9 +8980,9 @@
{{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
tapOnUnfocusedWindow();
- std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
+ const auto [downSequenceNum, downEvent] = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(downSequenceNum);
- std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent();
+ const auto [upSequenceNum, upEvent] = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(upSequenceNum);
// Don't finish the events yet, and send a key
// Injection will succeed because we will eventually give up and send the key to the focused
@@ -8863,8 +9045,7 @@
mFocusedWindow->consumeMotionDown();
// Focused window may or may not receive ACTION_MOVE
// But it should definitely receive ACTION_CANCEL due to the ANR
- InputEvent* event;
- std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event);
+ const auto [moveOrCancelSequenceNum, event] = mFocusedWindow->receiveEvent();
ASSERT_TRUE(moveOrCancelSequenceNum);
mFocusedWindow->finishEvent(*moveOrCancelSequenceNum);
ASSERT_NE(nullptr, event);
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 1ac043c..e9e5061 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -1557,4 +1557,122 @@
mousePc->assertPointerIconNotSet();
}
+TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) {
+ // Make sure there are two PointerControllers on different displays.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+ auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
+ auto secondMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, secondMousePc->getDisplayId());
+
+ // Both pointers should be visible.
+ ASSERT_TRUE(firstMousePc->isPointerShown());
+ ASSERT_TRUE(secondMousePc->isPointerShown());
+
+ // Hide the icon on the second display.
+ mChoreographer.setPointerIconVisibility(ANOTHER_DISPLAY_ID, false);
+ ASSERT_TRUE(firstMousePc->isPointerShown());
+ ASSERT_FALSE(secondMousePc->isPointerShown());
+
+ // Move and set pointer icons for both mice. The second pointer should still be hidden.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ firstMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ secondMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ ASSERT_TRUE(firstMousePc->isPointerShown());
+ ASSERT_FALSE(secondMousePc->isPointerShown());
+
+ // Allow the icon to be visible on the second display, and move the mouse.
+ mChoreographer.setPointerIconVisibility(ANOTHER_DISPLAY_ID, true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ ASSERT_TRUE(firstMousePc->isPointerShown());
+ ASSERT_TRUE(secondMousePc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // Hide the pointer on the display, and then connect the mouse.
+ mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, mousePc->getDisplayId());
+
+ // The pointer should not be visible.
+ ASSERT_FALSE(mousePc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // Hide the pointer on the display.
+ mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto touchpadPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, touchpadPc->getDisplayId());
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // The pointer should not be visible.
+ ASSERT_FALSE(touchpadPc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForStylus) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setStylusPointerIconEnabled(true);
+
+ // Hide the pointer on the display.
+ mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+
+ // The pointer should not be visible.
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
} // namespace android
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 9a23c84..bc178bc 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -80,7 +80,7 @@
// Check if a call to Power HAL function failed; if so, log the failure and
// invalidate the current Power HAL handle.
template <typename T>
-HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const char* fnName) {
+HalResult<T> PowerHalController::processHalResult(HalResult<T>&& result, const char* fnName) {
if (result.isFailed()) {
ALOGE("%s failed: %s", fnName, result.errorMessage());
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
@@ -88,21 +88,19 @@
mConnectedHal = nullptr;
mHalConnector->reset();
}
- return result;
+ return std::move(result);
}
HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) {
std::shared_ptr<HalWrapper> handle = initHal();
- auto result = handle->setBoost(boost, durationMs);
- return processHalResult(result, "setBoost");
+ return processHalResult(handle->setBoost(boost, durationMs), "setBoost");
}
HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode,
bool enabled) {
std::shared_ptr<HalWrapper> handle = initHal();
- auto result = handle->setMode(mode, enabled);
- return processHalResult(result, "setMode");
+ return processHalResult(handle->setMode(mode, enabled), "setMode");
}
HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
@@ -110,14 +108,35 @@
const std::vector<int32_t>& threadIds,
int64_t durationNanos) {
std::shared_ptr<HalWrapper> handle = initHal();
- auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
- return processHalResult(result, "createHintSession");
+ return processHalResult(handle->createHintSession(tgid, uid, threadIds, durationNanos),
+ "createHintSession");
+}
+
+HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+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");
}
HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() {
std::shared_ptr<HalWrapper> handle = initHal();
- auto result = handle->getHintSessionPreferredRate();
- return processHalResult(result, "getHintSessionPreferredRate");
+ return 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");
+}
+
+HalResult<void> PowerHalController::closeSessionChannel(int tgid, int uid) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ return processHalResult(handle->closeSessionChannel(tgid, uid), "closeSessionChannel");
}
} // namespace power
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 76afbfc..1009100 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -42,37 +42,58 @@
// -------------------------------------------------------------------------------------------------
HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
- ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
- toString(boost).c_str(), durationMs);
+ ALOGV("Skipped setBoost %s with duration %dms because %s", toString(boost).c_str(), durationMs,
+ getUnsupportedMessage());
return HalResult<void>::unsupported();
}
HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
- ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
- enabled ? "true" : "false");
+ ALOGV("Skipped setMode %s to %s because %s", toString(mode).c_str(), enabled ? "true" : "false",
+ getUnsupportedMessage());
return HalResult<void>::unsupported();
}
HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
- ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
- threadIds.size());
+ ALOGV("Skipped createHintSession(task num=%zu) because %s", threadIds.size(),
+ getUnsupportedMessage());
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> 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();
}
HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
- ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+ ALOGV("Skipped getHintSessionPreferredRate because %s", getUnsupportedMessage());
return HalResult<int64_t>::unsupported();
}
+HalResult<Aidl::ChannelConfig> EmptyHalWrapper::getSessionChannel(int, int) {
+ ALOGV("Skipped getSessionChannel because %s", getUnsupportedMessage());
+ return HalResult<Aidl::ChannelConfig>::unsupported();
+}
+
+HalResult<void> EmptyHalWrapper::closeSessionChannel(int, int) {
+ ALOGV("Skipped closeSessionChannel because %s", getUnsupportedMessage());
+ return HalResult<void>::unsupported();
+}
+
+const char* EmptyHalWrapper::getUnsupportedMessage() {
+ return "Power HAL is not supported";
+}
+
// -------------------------------------------------------------------------------------------------
HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) {
if (boost == Aidl::Boost::INTERACTION) {
return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs);
} else {
- ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
- return HalResult<void>::unsupported();
+ return EmptyHalWrapper::setBoost(boost, durationMs);
}
}
@@ -92,9 +113,7 @@
case Aidl::Mode::DOUBLE_TAP_TO_WAKE:
return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
default:
- ALOGV("Skipped setMode %s because Power HAL AIDL not available",
- toString(mode).c_str());
- return HalResult<void>::unsupported();
+ return EmptyHalWrapper::setMode(mode, enabled);
}
}
@@ -113,16 +132,8 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
- int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
- ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
- threadIds.size());
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
-}
-
-HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
- ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
- return HalResult<int64_t>::unsupported();
+const char* HidlHalWrapperV1_0::getUnsupportedMessage() {
+ return "Power HAL AIDL is not supported";
}
// -------------------------------------------------------------------------------------------------
@@ -191,7 +202,7 @@
// Quick return if boost is not supported by HAL
if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) {
- ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
+ ALOGV("Skipped setBoost %s because %s", toString(boost).c_str(), getUnsupportedMessage());
return HalResult<void>::unsupported();
}
@@ -207,8 +218,8 @@
mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
- ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
- toString(boost).c_str());
+ ALOGV("Skipped setBoost %s because %s", toString(boost).c_str(),
+ getUnsupportedMessage());
return HalResult<void>::unsupported();
}
}
@@ -223,7 +234,7 @@
// Quick return if mode is not supported by HAL
if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) {
- ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
+ ALOGV("Skipped setMode %s because %s", toString(mode).c_str(), getUnsupportedMessage());
return HalResult<void>::unsupported();
}
@@ -236,8 +247,7 @@
mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
- ALOGV("Skipped setMode %s because Power HAL doesn't support it",
- toString(mode).c_str());
+ ALOGV("Skipped setMode %s because %s", toString(mode).c_str(), getUnsupportedMessage());
return HalResult<void>::unsupported();
}
}
@@ -251,7 +261,17 @@
std::shared_ptr<Aidl::IPowerHintSession> appSession;
return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
- appSession);
+ std::move(appSession));
+}
+
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> 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>>::
+ fromStatus(mHandle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
+ tag, config, &appSession),
+ std::move(appSession));
}
HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() {
@@ -260,6 +280,20 @@
return HalResult<int64_t>::fromStatus(result, rate);
}
+HalResult<Aidl::ChannelConfig> AidlHalWrapper::getSessionChannel(int tgid, int uid) {
+ Aidl::ChannelConfig config;
+ auto result = mHandle->getSessionChannel(tgid, uid, &config);
+ return HalResult<Aidl::ChannelConfig>::fromStatus(result, std::move(config));
+}
+
+HalResult<void> AidlHalWrapper::closeSessionChannel(int tgid, int uid) {
+ return toHalResult(mHandle->closeSessionChannel(tgid, uid));
+}
+
+const char* AidlHalWrapper::getUnsupportedMessage() {
+ return "Power HAL doesn't support it";
+}
+
// -------------------------------------------------------------------------------------------------
} // namespace power
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 3d2cf29..a720296 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -256,6 +256,23 @@
ASSERT_TRUE(result.isOk());
}
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigSuccessful) {
+ 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(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ auto result =
+ mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
+ ASSERT_TRUE(result.isOk());
+}
+
TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) {
int32_t tgid = 999;
int32_t uid = 1001;
@@ -279,3 +296,18 @@
int64_t rate = result.value();
ASSERT_GE(0, rate);
}
+
+TEST_F(PowerHalWrapperAidlTest, TestSessionChannel) {
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ EXPECT_CALL(*mMockHal.get(), getSessionChannel(Eq(tgid), Eq(uid), _))
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), closeSessionChannel(Eq(tgid), Eq(uid)))
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ auto createResult = mWrapper->getSessionChannel(tgid, uid);
+ ASSERT_TRUE(createResult.isOk());
+ auto closeResult = mWrapper->closeSessionChannel(tgid, uid);
+ ASSERT_TRUE(closeResult.isOk());
+}
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 45fab7e..8e9dfea 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -16,13 +16,6 @@
#include "SensorDevice.h"
-#include "android/hardware/sensors/2.0/types.h"
-#include "android/hardware/sensors/2.1/types.h"
-#include "convertV2_1.h"
-
-#include "AidlSensorHalWrapper.h"
-#include "HidlSensorHalWrapper.h"
-
#include <android-base/logging.h>
#include <android/util/ProtoOutputStream.h>
#include <com_android_frameworks_sensorservice_flags.h>
@@ -36,10 +29,16 @@
#include <chrono>
#include <cinttypes>
-#include <cstddef>
-#include <thread>
-#include <mutex>
#include <condition_variable>
+#include <cstddef>
+#include <mutex>
+#include <thread>
+
+#include "AidlSensorHalWrapper.h"
+#include "HidlSensorHalWrapper.h"
+#include "android/hardware/sensors/2.0/types.h"
+#include "android/hardware/sensors/2.1/types.h"
+#include "convertV2_1.h"
using namespace android::hardware::sensors;
using android::util::ProtoOutputStream;
@@ -168,6 +167,9 @@
mActivationCount.clear();
mSensorList.clear();
+ if (sensorservice_flags::dynamic_sensor_hal_reconnect_handling()) {
+ mConnectedDynamicSensors.clear();
+ }
if (mHalWrapper->connect(this)) {
initializeSensorList();
@@ -342,6 +344,15 @@
}
}
+std::vector<int32_t> SensorDevice::getDynamicSensorHandles() {
+ std::vector<int32_t> sensorHandles;
+ std::lock_guard<std::mutex> lock(mDynamicSensorsMutex);
+ for (auto& sensors : mConnectedDynamicSensors) {
+ sensorHandles.push_back(sensors.first);
+ }
+ return sensorHandles;
+}
+
ssize_t SensorDevice::getSensorList(sensor_t const** list) {
*list = &mSensorList[0];
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index f127c0f..52f7cf2 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -60,6 +60,8 @@
ssize_t getSensorList(sensor_t const** list);
+ std::vector<int32_t> getDynamicSensorHandles();
+
void handleDynamicSensorConnection(int handle, bool connected);
status_t initCheck() const;
int getHalDeviceVersion() const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 85043c9..e1c43c6 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "SensorService.h"
+
#include <aidl/android/hardware/sensors/ISensors.h>
#include <android-base/strings.h>
#include <android/content/pm/IPackageManagerNative.h>
@@ -22,20 +24,36 @@
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <binder/PermissionController.h>
+#include <com_android_frameworks_sensorservice_flags.h>
#include <cutils/ashmem.h>
#include <cutils/misc.h>
#include <cutils/properties.h>
#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <hardware/sensors.h>
#include <hardware_legacy/power.h>
+#include <inttypes.h>
#include <log/log.h>
+#include <math.h>
#include <openssl/digest.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
+#include <private/android_filesystem_config.h>
+#include <sched.h>
#include <sensor/SensorEventQueue.h>
#include <sensorprivacy/SensorPrivacyManager.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/SystemClock.h>
+#include <condition_variable>
+#include <ctime>
+#include <future>
+#include <mutex>
+#include <string>
+
#include "BatteryService.h"
#include "CorrectedGyroSensor.h"
#include "GravitySensor.h"
@@ -43,35 +61,17 @@
#include "LinearAccelerationSensor.h"
#include "OrientationSensor.h"
#include "RotationVectorSensor.h"
-#include "SensorFusion.h"
-#include "SensorInterface.h"
-
-#include "SensorService.h"
#include "SensorDirectConnection.h"
#include "SensorEventAckReceiver.h"
#include "SensorEventConnection.h"
+#include "SensorFusion.h"
+#include "SensorInterface.h"
#include "SensorRecord.h"
#include "SensorRegistrationInfo.h"
#include "SensorServiceUtils.h"
-#include <inttypes.h>
-#include <math.h>
-#include <sched.h>
-#include <stdint.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <condition_variable>
-#include <ctime>
-#include <future>
-#include <mutex>
-#include <string>
-
-#include <private/android_filesystem_config.h>
-
using namespace std::chrono_literals;
+namespace sensorservice_flags = com::android::frameworks::sensorservice::flags;
namespace android {
// ---------------------------------------------------------------------------
@@ -335,6 +335,11 @@
case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
hasGyroUncalibrated = true;
break;
+ case SENSOR_TYPE_DYNAMIC_SENSOR_META:
+ if (sensorservice_flags::dynamic_sensor_hal_reconnect_handling()) {
+ mDynamicMetaSensorHandle = list[i].handle;
+ }
+ break;
case SENSOR_TYPE_GRAVITY:
case SENSOR_TYPE_LINEAR_ACCELERATION:
case SENSOR_TYPE_ROTATION_VECTOR:
@@ -1055,6 +1060,68 @@
}
}
+void SensorService::sendEventsToAllClients(
+ const std::vector<sp<SensorEventConnection>>& activeConnections,
+ ssize_t count) {
+ // Send our events to clients. Check the state of wake lock for each client
+ // and release the lock if none of the clients need it.
+ bool needsWakeLock = false;
+ for (const sp<SensorEventConnection>& connection : activeConnections) {
+ connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
+ mMapFlushEventsToConnections);
+ needsWakeLock |= connection->needsWakeLock();
+ // If the connection has one-shot sensors, it may be cleaned up after
+ // first trigger. Early check for one-shot sensors.
+ if (connection->hasOneShotSensors()) {
+ cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count);
+ }
+ }
+
+ if (mWakeLockAcquired && !needsWakeLock) {
+ setWakeLockAcquiredLocked(false);
+ }
+}
+
+void SensorService::disconnectDynamicSensor(
+ int handle,
+ const std::vector<sp<SensorEventConnection>>& activeConnections) {
+ ALOGI("Dynamic sensor handle 0x%x disconnected", handle);
+ SensorDevice::getInstance().handleDynamicSensorConnection(
+ handle, false /*connected*/);
+ if (!unregisterDynamicSensorLocked(handle)) {
+ ALOGE("Dynamic sensor release error.");
+ }
+ for (const sp<SensorEventConnection>& connection : activeConnections) {
+ connection->removeSensor(handle);
+ }
+}
+
+void SensorService::handleDeviceReconnection(SensorDevice& device) {
+ if (sensorservice_flags::dynamic_sensor_hal_reconnect_handling()) {
+ const std::vector<sp<SensorEventConnection>> activeConnections =
+ mConnectionHolder.lock(mLock).getActiveConnections();
+
+ for (int32_t handle : device.getDynamicSensorHandles()) {
+ if (mDynamicMetaSensorHandle.has_value()) {
+ // Sending one event at a time to prevent the number of handle is more than the
+ // buffer can hold.
+ mSensorEventBuffer[0].type = SENSOR_TYPE_DYNAMIC_SENSOR_META;
+ mSensorEventBuffer[0].sensor = *mDynamicMetaSensorHandle;
+ mSensorEventBuffer[0].dynamic_sensor_meta.connected = false;
+ mSensorEventBuffer[0].dynamic_sensor_meta.handle = handle;
+ mMapFlushEventsToConnections[0] = nullptr;
+
+ disconnectDynamicSensor(handle, activeConnections);
+ sendEventsToAllClients(activeConnections, 1);
+ } else {
+ ALOGE("Failed to find mDynamicMetaSensorHandle during init.");
+ break;
+ }
+ }
+ }
+ device.reconnect();
+}
+
bool SensorService::threadLoop() {
ALOGD("nuSensorService thread starting...");
@@ -1071,8 +1138,8 @@
do {
ssize_t count = device.poll(mSensorEventBuffer, numEventMax);
if (count < 0) {
- if(count == DEAD_OBJECT && device.isReconnecting()) {
- device.reconnect();
+ if (count == DEAD_OBJECT && device.isReconnecting()) {
+ handleDeviceReconnection(device);
continue;
} else {
ALOGE("sensor poll failed (%s)", strerror(-count));
@@ -1176,7 +1243,6 @@
rec->removeFirstPendingFlushConnection();
}
}
-
// handle dynamic sensor meta events, process registration and unregistration of dynamic
// sensor based on content of event.
if (mSensorEventBuffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) {
@@ -1206,37 +1272,14 @@
}
} else {
int handle = mSensorEventBuffer[i].dynamic_sensor_meta.handle;
- ALOGI("Dynamic sensor handle 0x%x disconnected", handle);
-
- device.handleDynamicSensorConnection(handle, false /*connected*/);
- if (!unregisterDynamicSensorLocked(handle)) {
- ALOGE("Dynamic sensor release error.");
- }
-
- for (const sp<SensorEventConnection>& connection : activeConnections) {
- connection->removeSensor(handle);
- }
+ disconnectDynamicSensor(handle, activeConnections);
}
}
}
// Send our events to clients. Check the state of wake lock for each client and release the
// lock if none of the clients need it.
- bool needsWakeLock = false;
- for (const sp<SensorEventConnection>& connection : activeConnections) {
- connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
- mMapFlushEventsToConnections);
- needsWakeLock |= connection->needsWakeLock();
- // If the connection has one-shot sensors, it may be cleaned up after first trigger.
- // Early check for one-shot sensors.
- if (connection->hasOneShotSensors()) {
- cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count);
- }
- }
-
- if (mWakeLockAcquired && !needsWakeLock) {
- setWakeLockAcquiredLocked(false);
- }
+ sendEventsToAllClients(activeConnections, count);
} while (!Thread::exitPending());
ALOGW("Exiting SensorService::threadLoop => aborting...");
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index b643f6b..ad10e7a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -17,9 +17,6 @@
#ifndef ANDROID_SENSOR_SERVICE_H
#define ANDROID_SENSOR_SERVICE_H
-#include "SensorList.h"
-#include "RecentEventLogger.h"
-
#include <android-base/macros.h>
#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
@@ -27,11 +24,11 @@
#include <cutils/compiler.h>
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
-#include <sensor/ISensorServer.h>
#include <sensor/ISensorEventConnection.h>
+#include <sensor/ISensorServer.h>
#include <sensor/Sensor.h>
-#include "android/hardware/BnSensorPrivacyListener.h"
-
+#include <stdint.h>
+#include <sys/types.h>
#include <utils/AndroidThreads.h>
#include <utils/KeyedVector.h>
#include <utils/Looper.h>
@@ -40,8 +37,6 @@
#include <utils/Vector.h>
#include <utils/threads.h>
-#include <stdint.h>
-#include <sys/types.h>
#include <condition_variable>
#include <mutex>
#include <queue>
@@ -49,6 +44,10 @@
#include <unordered_set>
#include <vector>
+#include "RecentEventLogger.h"
+#include "SensorList.h"
+#include "android/hardware/BnSensorPrivacyListener.h"
+
#if __clang__
// Clang warns about SensorEventConnection::dump hiding BBinder::dump. The cause isn't fixable
// without changing the API, so let's tell clang this is indeed intentional.
@@ -57,7 +56,7 @@
// ---------------------------------------------------------------------------
#define IGNORE_HARDWARE_FUSION false
-#define DEBUG_CONNECTIONS false
+#define DEBUG_CONNECTIONS false
// Max size is 100 KB which is enough to accept a batch of about 1000 events.
#define MAX_SOCKET_BUFFER_SIZE_BATCHED (100 * 1024)
// For older HALs which don't support batching, use a smaller socket buffer size.
@@ -453,6 +452,11 @@
// Send events from the event cache for this particular connection.
void sendEventsFromCache(const sp<SensorEventConnection>& connection);
+ // Send all events in the buffer to all clients.
+ void sendEventsToAllClients(
+ const std::vector<sp<SensorEventConnection>>& activeConnections,
+ ssize_t count);
+
// If SensorService is operating in RESTRICTED mode, only select whitelisted packages are
// allowed to register for or call flush on sensors. Typically only cts test packages are
// allowed.
@@ -516,6 +520,14 @@
bool isInjectionMode(int mode);
+ void handleDeviceReconnection(SensorDevice& device);
+
+ // Removes a connected dynamic sensor and send the corresponding event to
+ // all connections.
+ void disconnectDynamicSensor(
+ int handle,
+ const std::vector<sp<SensorEventConnection>>& activeConnections);
+
static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
}
@@ -583,6 +595,10 @@
bool mLastReportedProxIsActive;
// Listeners subscribed to receive updates on the proximity sensor active state.
std::vector<sp<ProximityActiveListener>> mProximityActiveListeners;
+
+ // Stores the handle of the dynamic_meta sensor to send clean up event once
+ // HAL crashes.
+ std::optional<int> mDynamicMetaSensorHandle;
};
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index c71c517..39748b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -60,7 +60,7 @@
virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
// Sends the brightness setting to HWC
- virtual void applyDisplayBrightness(const bool applyImmediately) = 0;
+ virtual void applyDisplayBrightness(bool applyImmediately) = 0;
protected:
~Display() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index c53b461..2dc9a1a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -73,7 +73,7 @@
const compositionengine::DisplayColorProfileCreationArgs&) override;
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
- void applyDisplayBrightness(const bool applyImmediately) override;
+ void applyDisplayBrightness(bool applyImmediately) override;
void setSecure(bool secure) override;
// Internal helpers used by chooseCompositionStrategy()
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index d907bf5..6428d08 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -204,13 +204,12 @@
setReleasedLayers(std::move(releasedLayers));
}
-void Display::applyDisplayBrightness(const bool applyImmediately) {
- auto& hwc = getCompositionEngine().getHwComposer();
- const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId());
- if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
- physicalDisplayId && getState().displayBrightness) {
+void Display::applyDisplayBrightness(bool applyImmediately) {
+ if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast);
+ displayId && getState().displayBrightness) {
+ auto& hwc = getCompositionEngine().getHwComposer();
const status_t result =
- hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
+ hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
getState().displayBrightnessNits,
Hwc2::Composer::DisplayBrightnessOptions{
.applyImmediately = applyImmediately})
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
index c0e77bb..d07cdf5 100644
--- a/services/surfaceflinger/Display/DisplayModeRequest.h
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -27,9 +27,6 @@
// Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
bool emitEvent = false;
-
- // Whether to force the request to be applied, even if the mode is unchanged.
- bool force = false;
};
inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) {
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b96264b..4f81482 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -24,7 +24,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -68,6 +67,7 @@
mActiveModeFpsTrace(concatId("ActiveModeFps")),
mRenderRateFpsTrace(concatId("RenderRateFps")),
mPhysicalOrientation(args.physicalOrientation),
+ mPowerMode(ftl::Concat("PowerMode ", getId().value).c_str(), args.initialPowerMode),
mIsPrimary(args.isPrimary),
mRequestedRefreshRate(args.requestedRefreshRate),
mRefreshRateSelector(std::move(args.refreshRateSelector)),
@@ -106,9 +106,7 @@
mCompositionDisplay->getRenderSurface()->initialize();
- if (const auto powerModeOpt = args.initialPowerMode) {
- setPowerMode(*powerModeOpt);
- }
+ setPowerMode(args.initialPowerMode);
// initialize the display orientation transform.
setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -173,6 +171,7 @@
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
+ // TODO(b/241285876): Skip this for virtual displays.
if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
if (mStagedBrightness && mBrightness != mStagedBrightness) {
getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
@@ -182,33 +181,26 @@
getCompositionDisplay()->applyDisplayBrightness(true);
}
- if (mPowerMode) {
- *mPowerMode = mode;
- } else {
- mPowerMode.emplace("PowerMode -" + to_string(getId()), mode);
- }
+ mPowerMode = mode;
getCompositionDisplay()->setCompositionEnabled(isPoweredOn());
}
void DisplayDevice::tracePowerMode() {
- // assign the same value for tracing
- if (mPowerMode) {
- const hal::PowerMode powerMode = *mPowerMode;
- *mPowerMode = powerMode;
- }
+ // Assign the same value for tracing.
+ mPowerMode = mPowerMode.get();
}
void DisplayDevice::enableLayerCaching(bool enable) {
getCompositionDisplay()->setLayerCachingEnabled(enable);
}
-std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const {
+hal::PowerMode DisplayDevice::getPowerMode() const {
return mPowerMode;
}
bool DisplayDevice::isPoweredOn() const {
- return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
+ return mPowerMode != hal::PowerMode::OFF;
}
void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
@@ -222,17 +214,6 @@
bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline& outTimeline) {
- // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For
- // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared
- // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed
- // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`.
- if (FlagManager::getInstance().connected_display()) {
- std::scoped_lock lock(mDesiredModeLock);
- if (mDesiredModeOpt) {
- mDesiredModeOpt->force = false;
- }
- }
-
mPendingModeOpt = std::move(desiredMode);
mIsModeSetPending = true;
@@ -538,7 +519,8 @@
}
}
-auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction {
+auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force)
+ -> DesiredModeAction {
ATRACE_CALL();
const auto& desiredModePtr = desiredMode.mode.modePtr;
@@ -546,26 +528,20 @@
LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(),
"DisplayId mismatch");
- // TODO (b/318533819): Stringize DisplayModeRequest.
- ALOGD("%s(%s, force=%s)", __func__, to_string(*desiredModePtr).c_str(),
- desiredMode.force ? "true" : "false");
+ ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str());
std::scoped_lock lock(mDesiredModeLock);
if (mDesiredModeOpt) {
// A mode transition was already scheduled, so just override the desired mode.
const bool emitEvent = mDesiredModeOpt->emitEvent;
- const bool force = mDesiredModeOpt->force;
mDesiredModeOpt = std::move(desiredMode);
mDesiredModeOpt->emitEvent |= emitEvent;
- if (FlagManager::getInstance().connected_display()) {
- mDesiredModeOpt->force |= force;
- }
return DesiredModeAction::None;
}
// If the desired mode is already active...
const auto activeMode = refreshRateSelector().getActiveMode();
- if (!desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
+ if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
if (activeMode == desiredMode.mode) {
return DesiredModeAction::None;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 46534de..4ab6321 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -173,8 +173,8 @@
/* ------------------------------------------------------------------------
* Display power mode management.
*/
- std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const;
- void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
+ hardware::graphics::composer::hal::PowerMode getPowerMode() const;
+ void setPowerMode(hardware::graphics::composer::hal::PowerMode);
bool isPoweredOn() const;
void tracePowerMode();
@@ -189,7 +189,8 @@
enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
- DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock);
+ DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false)
+ EXCLUDES(mDesiredModeLock);
using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>;
@@ -270,9 +271,7 @@
ui::Rotation mOrientation = ui::ROTATION_0;
bool mIsOrientationChanged = false;
- // Allow nullopt as initial power mode.
- using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
- std::optional<TracedPowerMode> mPowerMode;
+ TracedOrdinal<hardware::graphics::composer::hal::PowerMode> mPowerMode;
std::optional<float> mStagedBrightness;
std::optional<float> mBrightness;
@@ -362,7 +361,8 @@
HdrCapabilities hdrCapabilities;
int32_t supportedPerFrameMetadata{0};
std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
- std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
+ hardware::graphics::composer::hal::PowerMode initialPowerMode{
+ hardware::graphics::composer::hal::PowerMode::OFF};
bool isPrimary{false};
DisplayModeId activeModeId;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 64a8ae7..17f6f31 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -330,7 +330,11 @@
t.join();
close(pipefds[0]);
- return str;
+
+ std::string hash;
+ mAidlComposer->getInterfaceHash(&hash);
+ return std::string(mAidlComposer->descriptor) +
+ " version:" + std::to_string(mComposerInterfaceVersion) + " hash:" + hash + str;
}
void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index db66f5b..704ece5 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -27,7 +27,6 @@
#include "HWC2.h"
#include <android/configuration.h>
-#include <common/FlagManager.h>
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
@@ -417,19 +416,7 @@
VsyncPeriodChangeTimeline* outTimeline) {
ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
- // FIXME (b/319505580): At least the first config set on an external display must be
- // `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints`
- // for simplicity.
- ui::DisplayConnectionType type = ui::DisplayConnectionType::Internal;
- const bool connected_display = FlagManager::getInstance().connected_display();
- if (connected_display) {
- if (auto err = getConnectionType(&type); err != Error::NONE) {
- return err;
- }
- }
-
- if (isVsyncPeriodSwitchSupported() &&
- (!connected_display || type != ui::DisplayConnectionType::External)) {
+ if (isVsyncPeriodSwitchSupported()) {
Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c4ff9cc..12ab2c2 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -291,6 +291,7 @@
std::string HidlComposer::dumpDebugInfo() {
std::string info;
+ info += std::string(mComposer->descriptor) + "\n";
mComposer->dumpDebugInfo([&](const auto& tmpInfo) { info = tmpInfo.c_str(); });
return info;
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index 155cf4d..a47348f 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -26,13 +26,12 @@
namespace android {
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
- std::unique_ptr<Clock> clock)
- : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) {
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, std::unique_ptr<Clock> clock)
+ : mFrameTimeline(frameTimeline), mClock(std::move(clock)) {
LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!");
}
-void FpsReporter::dispatchLayerFps() {
+void FpsReporter::dispatchLayerFps(const frontend::LayerHierarchy& layerHierarchy) {
const auto now = mClock->now();
if (now - mLastDispatch < kMinDispatchDuration) {
return;
@@ -52,31 +51,42 @@
}
std::unordered_set<int32_t> seenTasks;
- std::vector<std::pair<TrackedListener, sp<Layer>>> listenersAndLayersToReport;
+ std::vector<std::pair<TrackedListener, const frontend::LayerHierarchy*>>
+ listenersAndLayersToReport;
- mFlinger.mCurrentState.traverse([&](Layer* layer) {
- auto& currentState = layer->getDrawingState();
- if (currentState.metadata.has(gui::METADATA_TASK_ID)) {
- int32_t taskId = currentState.metadata.getInt32(gui::METADATA_TASK_ID, 0);
+ layerHierarchy.traverse([&](const frontend::LayerHierarchy& hierarchy,
+ const frontend::LayerHierarchy::TraversalPath& traversalPath) {
+ if (traversalPath.variant == frontend::LayerHierarchy::Variant::Detached) {
+ return false;
+ }
+ const auto& metadata = hierarchy.getLayer()->metadata;
+ if (metadata.has(gui::METADATA_TASK_ID)) {
+ int32_t taskId = metadata.getInt32(gui::METADATA_TASK_ID, 0);
if (seenTasks.count(taskId) == 0) {
// localListeners is expected to be tiny
for (TrackedListener& listener : localListeners) {
if (listener.taskId == taskId) {
seenTasks.insert(taskId);
- listenersAndLayersToReport.push_back(
- {listener, sp<Layer>::fromExisting(layer)});
+ listenersAndLayersToReport.push_back({listener, &hierarchy});
break;
}
}
}
}
+ return true;
});
- for (const auto& [listener, layer] : listenersAndLayersToReport) {
+ for (const auto& [listener, hierarchy] : listenersAndLayersToReport) {
std::unordered_set<int32_t> layerIds;
- layer->traverse(LayerVector::StateSet::Current,
- [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+ hierarchy->traverse([&](const frontend::LayerHierarchy& hierarchy,
+ const frontend::LayerHierarchy::TraversalPath& traversalPath) {
+ if (traversalPath.variant == frontend::LayerHierarchy::Variant::Detached) {
+ return false;
+ }
+ layerIds.insert(static_cast<int32_t>(hierarchy.getLayer()->id));
+ return true;
+ });
listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
}
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index 438b1aa..01f1e07 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -24,6 +24,7 @@
#include "Clock.h"
#include "FrameTimeline/FrameTimeline.h"
+#include "FrontEnd/LayerHierarchy.h"
#include "WpHash.h"
namespace android {
@@ -33,13 +34,13 @@
class FpsReporter : public IBinder::DeathRecipient {
public:
- FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline,
std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
// Dispatches updated layer fps values for the registered listeners
// This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
// must be held when calling this method.
- void dispatchLayerFps() EXCLUDES(mMutex);
+ void dispatchLayerFps(const frontend::LayerHierarchy&) EXCLUDES(mMutex);
// Override for IBinder::DeathRecipient
void binderDied(const wp<IBinder>&) override;
@@ -58,7 +59,6 @@
};
frametimeline::FrameTimeline& mFrameTimeline;
- SurfaceFlinger& mFlinger;
static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration =
std::chrono::milliseconds(500);
std::unique_ptr<Clock> mClock;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index bb32afa..8df5d8c 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -587,8 +587,8 @@
bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
if (!mResortSnapshots && args.forceUpdate == ForceUpdateFlags::NONE &&
!args.layerLifecycleManager.getGlobalChanges().any(
- RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Visibility)) {
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::Input)) {
// We are not force updating and there are no hierarchy or visibility changes. Avoid sorting
// the snapshots.
return false;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c8b1059..219a8e2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -78,11 +78,13 @@
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
+#include "Utils/FenceUtils.h"
#define DEBUG_RESIZE 0
#define EARLY_RELEASE_ENABLED false
namespace android {
+using namespace std::chrono_literals;
namespace {
constexpr int kDumpTableRowLength = 159;
@@ -2911,7 +2913,8 @@
}
void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack) {
+ ui::LayerStack layerStack,
+ std::function<FenceResult(FenceResult)>&& continuation) {
// If we are displayed on multiple displays in a single composition cycle then we would
// need to do careful tracking to enable the use of the mLastClientCompositionFence.
// For example we can only use it if all the displays are client comp, and we need
@@ -2946,11 +2949,41 @@
break;
}
}
+
+ if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
+ futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
+ }
+
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
ch->name = mName;
+ } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
+ // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
+ // asynchronously, then make sure we don't drop the fence.
+ mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult),
+ std::move(continuation));
+ std::vector<FenceAndContinuation> mergedFences;
+ sp<Fence> prevFence = nullptr;
+ // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
+ // grow unbounded.
+ for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
+ auto result = futureAndContinution.future.wait_for(0s);
+ if (result != std::future_status::ready) {
+ mergedFences.emplace_back(futureAndContinution);
+ continue;
+ }
+
+ mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE),
+ prevFence);
+ }
+ if (prevFence != nullptr) {
+ mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
+ }
+
+ mAdditionalPreviousReleaseFences.swap(mergedFences);
}
+
if (mBufferInfo.mBuffer) {
mPreviouslyPresentedLayerStacks.push_back(layerStack);
}
@@ -3440,6 +3473,15 @@
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
handle->previousFrameNumber = mDrawingState.previousFrameNumber;
+ if (FlagManager::getInstance().screenshot_fence_preservation() &&
+ mPreviousReleaseBufferEndpoint == handle->listener) {
+ // Add fences from previous screenshots now so that they can be dispatched to the
+ // client.
+ for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
+ handle->previousReleaseFences.emplace_back(futureAndContinution.chain());
+ }
+ mAdditionalPreviousReleaseFences.clear();
+ }
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c772e0e..dfd57c6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -555,7 +555,8 @@
const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
void onPreComposition(nsecs_t refreshStartTime);
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack);
+ void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
+ std::function<FenceResult(FenceResult)>&& continuation = nullptr);
void setWasClientComposed(const sp<Fence>& fence) {
mLastClientCompositionFence = fence;
@@ -932,6 +933,19 @@
// the release fences from the correct displays when we release the last buffer
// from the layer.
std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
+ struct FenceAndContinuation {
+ ftl::SharedFuture<FenceResult> future;
+ std::function<FenceResult(FenceResult)> continuation;
+
+ ftl::SharedFuture<FenceResult> chain() const {
+ if (continuation) {
+ return ftl::Future(future).then(continuation).share();
+ } else {
+ return future;
+ }
+ }
+ };
+ std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences;
// Exposed so SurfaceFlinger can assert that it's held
const sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index cf8b3bf..a145e59 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -75,9 +75,9 @@
mHandler->dispatchFrame(vsyncId, expectedVsyncTime);
}
-void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch,
- frametimeline::TokenManager& tokenManager,
- std::chrono::nanoseconds workDuration) {
+void MessageQueue::initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch> dispatch,
+ frametimeline::TokenManager& tokenManager,
+ std::chrono::nanoseconds workDuration) {
std::unique_ptr<scheduler::VSyncCallbackRegistration> oldRegistration;
{
std::lock_guard lock(mVsync.mutex);
@@ -87,7 +87,7 @@
}
// See comments in onNewVsyncSchedule. Today, oldRegistration should be
- // empty, but nothing prevents us from calling initVsync multiple times, so
+ // empty, but nothing prevents us from calling initVsyncInternal multiple times, so
// go ahead and destruct it outside the lock for safety.
oldRegistration.reset();
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index a523147..edb424b 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -65,8 +65,9 @@
public:
virtual ~MessageQueue() = default;
- virtual void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
- std::chrono::nanoseconds workDuration) = 0;
+ virtual void initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch>,
+ frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) = 0;
virtual void destroyVsync() = 0;
virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
virtual void waitMessage() = 0;
@@ -137,8 +138,8 @@
public:
explicit MessageQueue(ICompositor&);
- void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
- std::chrono::nanoseconds workDuration) override;
+ void initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) override;
void destroyVsync() override;
void setDuration(std::chrono::nanoseconds workDuration) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 67e1b9c..d51af9a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -49,10 +49,10 @@
public:
// TODO(b/185535769): Inject clock to avoid sleeping in tests.
- RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate, PowerMode currentPowerMode)
+ RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate)
: mTimeStats(timeStats),
mCurrentRefreshRate(currentRefreshRate),
- mCurrentPowerMode(currentPowerMode) {}
+ mCurrentPowerMode(PowerMode::OFF) {}
void setPowerMode(PowerMode mode) {
if (mCurrentPowerMode == mode) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 27ca17f..c76d4bd 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -77,8 +77,7 @@
mFeatures(features),
mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)),
mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())),
- mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate,
- hal::PowerMode::OFF)),
+ mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate)),
mSchedulerCallback(callback),
mVsyncTrackerCallback(vsyncTrackerCallback) {}
@@ -96,6 +95,11 @@
demotePacesetterDisplay();
}
+void Scheduler::initVsync(frametimeline::TokenManager& tokenManager,
+ std::chrono::nanoseconds workDuration) {
+ Impl::initVsyncInternal(getVsyncSchedule()->getDispatch(), tokenManager, workDuration);
+}
+
void Scheduler::startTimers() {
using namespace sysprop;
using namespace std::string_literals;
@@ -642,13 +646,13 @@
return Fps{};
}
const Display& display = *displayOpt;
- const nsecs_t threshold =
- display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2;
- const nsecs_t nextVsyncTime =
- display.schedulePtr->getTracker()
- .nextAnticipatedVSyncTimeFrom(currentExpectedPresentTime.ns() + threshold,
- currentExpectedPresentTime.ns());
- return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns());
+ const Duration threshold =
+ display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriod() / 2;
+ const TimePoint nextVsyncTime =
+ display.schedulePtr->vsyncDeadlineAfter(currentExpectedPresentTime + threshold,
+ currentExpectedPresentTime);
+ const Duration frameInterval = nextVsyncTime - currentExpectedPresentTime;
+ return Fps::fromPeriodNsecs(frameInterval.ns());
}
void Scheduler::resync() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f62f1ba..9912622 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -131,7 +131,7 @@
void run();
- using Impl::initVsync;
+ void initVsync(frametimeline::TokenManager&, std::chrono::nanoseconds workDuration);
using Impl::getScheduledFrameTime;
using Impl::setDuration;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index db6a187..3491600 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -89,8 +89,12 @@
return period();
}
-TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const {
- return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns()));
+TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint,
+ ftl::Optional<TimePoint> lastVsyncOpt) const {
+ return TimePoint::fromNs(
+ mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns(),
+ lastVsyncOpt.transform(
+ [](TimePoint t) { return t.ns(); })));
}
void VsyncSchedule::dump(std::string& out) const {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 722ea0b..6f656d2 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -63,7 +63,8 @@
// IVsyncSource overrides:
Period period() const override;
- TimePoint vsyncDeadlineAfter(TimePoint) const override;
+ TimePoint vsyncDeadlineAfter(TimePoint,
+ ftl::Optional<TimePoint> lastVsyncOpt = {}) const override;
Period minFramePeriod() const override;
// Inform the schedule that the display mode changed the schedule needs to recalibrate
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
index 0154060..f0f7a87 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
@@ -16,13 +16,14 @@
#pragma once
+#include <ftl/optional.h>
#include <scheduler/Time.h>
namespace android::scheduler {
struct IVsyncSource {
virtual Period period() const = 0;
- virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0;
+ virtual TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const = 0;
virtual Period minFramePeriod() const = 0;
protected:
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index a9abcaf..29711af 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -38,7 +38,9 @@
const TimePoint vsyncDeadline;
Period period() const override { return vsyncPeriod; }
- TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; }
+ TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const override {
+ return vsyncDeadline;
+ }
Period minFramePeriod() const override { return framePeriod; }
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 31d120d..aa6be36 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -116,6 +116,7 @@
#include <common/FlagManager.h>
#include <gui/LayerStatePermissions.h>
#include <gui/SchedulingPolicy.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
@@ -492,14 +493,6 @@
mSupportsBlur = supportsBlurs;
ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
- const size_t defaultListSize = MAX_LAYERS;
- auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
- mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
- mGraphicBufferProducerListSizeLogThreshold =
- std::max(static_cast<int>(0.95 *
- static_cast<double>(mMaxGraphicBufferProducerListSize)),
- 1);
-
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -806,22 +799,26 @@
}));
}
-static std::optional<renderengine::RenderEngine::RenderEngineType>
-chooseRenderEngineTypeViaSysProp() {
+void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) {
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
if (strcmp(prop, "skiagl") == 0) {
- return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ builder.setThreaded(renderengine::RenderEngine::Threaded::NO)
+ .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL);
} else if (strcmp(prop, "skiaglthreaded") == 0) {
- return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+ builder.setThreaded(renderengine::RenderEngine::Threaded::YES)
+ .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL);
} else if (strcmp(prop, "skiavk") == 0) {
- return renderengine::RenderEngine::RenderEngineType::SKIA_VK;
+ builder.setThreaded(renderengine::RenderEngine::Threaded::NO)
+ .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
} else if (strcmp(prop, "skiavkthreaded") == 0) {
- return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED;
+ builder.setThreaded(renderengine::RenderEngine::Threaded::YES)
+ .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
} else {
- ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop);
- return {};
+ builder.setGraphicsApi(FlagManager::getInstance().vulkan_renderengine()
+ ? renderengine::RenderEngine::GraphicsApi::VK
+ : renderengine::RenderEngine::GraphicsApi::GL);
}
}
@@ -847,9 +844,7 @@
useContextPriority
? renderengine::RenderEngine::ContextPriority::REALTIME
: renderengine::RenderEngine::ContextPriority::MEDIUM);
- if (auto type = chooseRenderEngineTypeViaSysProp()) {
- builder.setRenderEngineType(type.value());
- }
+ chooseRenderEngineType(builder);
mRenderEngine = renderengine::RenderEngine::create(builder.build());
mCompositionEngine->setRenderEngine(mRenderEngine.get());
mMaxRenderTargetSize =
@@ -1235,10 +1230,8 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) {
- const auto mode = desiredMode.mode;
- const auto displayId = mode.modePtr->getPhysicalDisplayId();
-
+void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) {
+ const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto display = getDisplayDeviceLocked(displayId);
@@ -1247,9 +1240,10 @@
return;
}
- const bool emitEvent = desiredMode.emitEvent;
+ const auto mode = request.mode;
+ const bool emitEvent = request.emitEvent;
- switch (display->setDesiredMode(std::move(desiredMode))) {
+ switch (display->setDesiredMode(std::move(request), force)) {
case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
// DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId,
@@ -1435,8 +1429,7 @@
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
to_string(display->getId()).c_str());
- if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
- display->getActiveMode() == desiredModeOpt->mode) {
+ if (display->getActiveMode() == desiredModeOpt->mode) {
applyActiveMode(display);
continue;
}
@@ -1960,6 +1953,7 @@
const char* const whence = __func__;
return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
+ // TODO(b/241285876): Validate that the display is physical instead of failing later.
if (const auto display = getDisplayDeviceLocked(displayToken)) {
const bool supportsDisplayBrightnessCommand =
getHwComposer().getComposer()->isSupported(
@@ -2009,7 +2003,6 @@
Hwc2::Composer::DisplayBrightnessOptions{
.applyImmediately = true});
}
-
} else {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
return ftl::yield<status_t>(NAME_NOT_FOUND);
@@ -3105,7 +3098,7 @@
{
Mutex::Autolock lock(mStateLock);
if (mFpsReporter) {
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
}
if (mTunnelModeEnabledReporter) {
@@ -3302,88 +3295,14 @@
std::vector<HWComposer::HWCDisplayMode> hwcModes;
std::optional<hal::HWDisplayId> activeModeHwcId;
- const bool isExternalDisplay = FlagManager::getInstance().connected_display() &&
- getHwComposer().getDisplayConnectionType(displayId) ==
- ui::DisplayConnectionType::External;
-
int attempt = 0;
constexpr int kMaxAttempts = 3;
do {
hwcModes = getHwComposer().getModes(displayId,
scheduler::RefreshRateSelector::kMinSupportedFrameRate
.getPeriodNsecs());
-
activeModeHwcId = getHwComposer().getActiveMode(displayId);
- if (isExternalDisplay) {
- constexpr nsecs_t k59HzVsyncPeriod = 16949153;
- constexpr nsecs_t k60HzVsyncPeriod = 16666667;
-
- // DM sets the initial mode for an external display to 1080p@60, but
- // this comes after SF creates its own state (including the
- // DisplayDevice). For now, pick the same mode in order to avoid
- // inconsistent state and unnecessary mode switching.
- // TODO (b/318534874): Let DM decide the initial mode.
- //
- // Try to find 1920x1080 @ 60 Hz
- if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(),
- [](const auto& mode) {
- return mode.width == 1920 &&
- mode.height == 1080 &&
- mode.vsyncPeriod == k60HzVsyncPeriod;
- });
- iter != hwcModes.end()) {
- activeModeHwcId = iter->hwcId;
- break;
- }
-
- // Try to find 1920x1080 @ 59-60 Hz
- if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(),
- [](const auto& mode) {
- return mode.width == 1920 &&
- mode.height == 1080 &&
- mode.vsyncPeriod >= k60HzVsyncPeriod &&
- mode.vsyncPeriod <= k59HzVsyncPeriod;
- });
- iter != hwcModes.end()) {
- activeModeHwcId = iter->hwcId;
- break;
- }
-
- // The display does not support 1080p@60, and this is the last attempt to pick a display
- // mode. Prefer 60 Hz if available, with the closest resolution to 1080p.
- if (attempt + 1 == kMaxAttempts) {
- std::vector<HWComposer::HWCDisplayMode> hwcModeOpts;
-
- for (const auto& mode : hwcModes) {
- if (mode.width <= 1920 && mode.height <= 1080 &&
- mode.vsyncPeriod >= k60HzVsyncPeriod &&
- mode.vsyncPeriod <= k59HzVsyncPeriod) {
- hwcModeOpts.push_back(mode);
- }
- }
-
- if (const auto iter = std::max_element(hwcModeOpts.begin(), hwcModeOpts.end(),
- [](const auto& a, const auto& b) {
- const auto aSize = a.width * a.height;
- const auto bSize = b.width * b.height;
- if (aSize < bSize)
- return true;
- else if (aSize == bSize)
- return a.vsyncPeriod > b.vsyncPeriod;
- else
- return false;
- });
- iter != hwcModeOpts.end()) {
- activeModeHwcId = iter->hwcId;
- break;
- }
-
- // hwcModeOpts was empty, use hwcModes[0] as the last resort
- activeModeHwcId = hwcModes[0].hwcId;
- }
- }
-
const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
return mode.hwcId == activeModeHwcId;
};
@@ -3443,10 +3362,6 @@
return pair.second->getHwcId() == activeModeHwcId;
})->second;
- if (isExternalDisplay) {
- ALOGI("External display %s initial mode: {%s}", to_string(displayId).c_str(),
- to_string(*activeMode).c_str());
- }
return {modes, activeMode};
}
@@ -3632,9 +3547,7 @@
getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
- // virtual displays are always considered enabled
- creationArgs.initialPowerMode =
- state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt;
+ creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
creationArgs.requestedRefreshRate = state.requestedRefreshRate;
@@ -3753,27 +3666,6 @@
}
mDisplays.try_emplace(displayToken, std::move(display));
-
- // For an external display, loadDisplayModes already selected the same mode
- // as DM, but SF still needs to be updated to match.
- // TODO (b/318534874): Let DM decide the initial mode.
- if (const auto& physical = state.physical;
- mScheduler && physical && FlagManager::getInstance().connected_display()) {
- const bool isInternalDisplay = mPhysicalDisplays.get(physical->id)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- if (!isInternalDisplay) {
- auto activeModePtr = physical->activeMode;
- const auto fps = activeModePtr->getPeakFps();
-
- setDesiredMode(
- {.mode = scheduler::FrameRateMode{fps,
- ftl::as_non_null(std::move(activeModePtr))},
- .emitEvent = false,
- .force = true});
- }
- }
}
void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
@@ -4342,13 +4234,12 @@
/* workDuration */ activeRefreshRate.getPeriod(),
/* readyDuration */ configs.late.sfWorkDuration);
- mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(),
- *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
+ mScheduler->initVsync(*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
mRegionSamplingThread =
sp<RegionSamplingThread>::make(*this,
RegionSamplingThread::EnvironmentTimingTunables());
- mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
+ mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline);
}
void SurfaceFlinger::doCommitTransactions() {
@@ -5920,12 +5811,6 @@
}
void SurfaceFlinger::initializeDisplays() {
- const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
- if (!display) return;
-
- const sp<IBinder> token = display->getDisplayToken().promote();
- LOG_ALWAYS_FATAL_IF(token == nullptr);
-
TransactionState state;
state.inputWindowCommands = mInputWindowCommands;
const nsecs_t now = systemTime();
@@ -5936,18 +5821,10 @@
const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++;
state.id = transactionId;
- // reset screen orientation and use primary layer stack
- DisplayState d;
- d.what = DisplayState::eDisplayProjectionChanged |
- DisplayState::eLayerStackChanged;
- d.token = token;
- d.layerStack = ui::DEFAULT_LAYER_STACK;
- d.orientation = ui::ROTATION_0;
- d.orientedDisplaySpaceRect.makeInvalid();
- d.layerStackSpaceRect.makeInvalid();
- d.width = 0;
- d.height = 0;
- state.displays.add(d);
+ auto layerStack = ui::DEFAULT_LAYER_STACK.id;
+ for (const auto& [id, display] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) {
+ state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
+ }
std::vector<TransactionState> transactions;
transactions.emplace_back(state);
@@ -5960,12 +5837,25 @@
{
ftl::FakeGuard guard(mStateLock);
- setPowerModeInternal(display, hal::PowerMode::ON);
+
+ // In case of a restart, ensure all displays are off.
+ for (const auto& [id, display] : mPhysicalDisplays) {
+ setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
+ }
+
+ // Power on all displays. The primary display is first, so becomes the active display. Also,
+ // the DisplayCapability set of a display is populated on its first powering on. Do this now
+ // before responding to any Binder query from DisplayManager about display capabilities.
+ for (const auto& [id, display] : mPhysicalDisplays) {
+ setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON);
+ }
}
}
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
if (display->isVirtual()) {
+ // TODO(b/241285876): This code path should not be reachable, so enforce this at compile
+ // time.
ALOGE("%s: Invalid operation on virtual display", __func__);
return;
}
@@ -5973,8 +5863,8 @@
const auto displayId = display->getPhysicalId();
ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
- const auto currentModeOpt = display->getPowerMode();
- if (currentModeOpt == mode) {
+ const auto currentMode = display->getPowerMode();
+ if (currentMode == mode) {
return;
}
@@ -5991,7 +5881,7 @@
display->setPowerMode(mode);
const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
- if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
+ if (currentMode == hal::PowerMode::OFF) {
// Turn on the display
// Activate the display (which involves a modeset to the active mode) when the inner or
@@ -6036,7 +5926,7 @@
mVisibleRegionsDirty = true;
scheduleComposite(FrameHint::kActive);
} else if (mode == hal::PowerMode::OFF) {
- const bool currentModeNotDozeSuspend = (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND);
+ const bool currentModeNotDozeSuspend = (currentMode != hal::PowerMode::DOZE_SUSPEND);
// Turn off the display
if (displayId == mActiveDisplayId) {
if (const auto display = getActivatableDisplay()) {
@@ -6077,7 +5967,7 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (*currentModeOpt == hal::PowerMode::DOZE_SUSPEND &&
+ if (currentMode == hal::PowerMode::DOZE_SUSPEND &&
(displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
if (displayId == mActiveDisplayId) {
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
@@ -7850,6 +7740,12 @@
kAllowProtected, kGrayscale, captureListener);
}
+ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
+ sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
+ captureLayers(args, captureListener);
+ return captureListener->waitForResults();
+}
+
void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -8259,23 +8155,27 @@
//
// TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
// to CompositionEngine::present.
- const bool renderEngineIsThreaded = [&]() {
- using Type = renderengine::RenderEngine::RenderEngineType;
- const auto type = mRenderEngine->getRenderEngineType();
- return type == Type::SKIA_GL_THREADED;
- }();
- auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share()
- : ftl::yield(present()).share();
+ auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
+ : ftl::yield(present()).share();
for (auto& [layer, layerFE] : layers) {
- layer->onLayerDisplayed(ftl::Future(presentFuture)
- .then([layerFE = std::move(layerFE)](FenceResult) {
- return layerFE->stealCompositionResult()
- .releaseFences.back()
- .first.get();
- })
- .share(),
- ui::INVALID_LAYER_STACK);
+ layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
+ [layerFE = std::move(layerFE)](FenceResult) {
+ if (FlagManager::getInstance()
+ .screenshot_fence_preservation()) {
+ const auto compositionResult =
+ layerFE->stealCompositionResult();
+ const auto& fences = compositionResult.releaseFences;
+ // CompositionEngine may choose to cull layers that
+ // aren't visible, so pass a non-fence.
+ return fences.empty() ? Fence::NO_FENCE
+ : fences.back().first.get();
+ } else {
+ return layerFE->stealCompositionResult()
+ .releaseFences.back()
+ .first.get();
+ }
+ });
}
return presentFuture;
@@ -8442,7 +8342,7 @@
return INVALID_OPERATION;
}
- setDesiredMode({std::move(preferredMode), .emitEvent = true, .force = force});
+ setDesiredMode({std::move(preferredMode), .emitEvent = true}, force);
// Update the frameRateOverride list as the display render rate might have changed
if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) {
@@ -9694,6 +9594,12 @@
return binderStatusFromStatusT(NO_ERROR);
}
+binder::Status SurfaceComposerAIDL::captureLayersSync(const LayerCaptureArgs& args,
+ ScreenCaptureResults* outResults) {
+ *outResults = mFlinger->captureLayersSync(args);
+ return binderStatusFromStatusT(NO_ERROR);
+}
+
binder::Status SurfaceComposerAIDL::captureLayers(
const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
mFlinger->captureLayers(args, captureListener);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b23b519..c8e2a4d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -560,6 +560,7 @@
void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
void captureDisplay(DisplayId, const CaptureArgs&, const sp<IScreenCaptureListener>&);
+ ScreenCaptureResults captureLayersSync(const LayerCaptureArgs&);
void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
@@ -718,7 +719,7 @@
// Show hdr sdr ratio overlay
bool mHdrSdrRatioOverlay = false;
- void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ void setDesiredMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
Fps maxFps);
@@ -903,7 +904,8 @@
* Display and layer stack management
*/
- // Called during boot, and restart after system_server death.
+ // Called during boot and restart after system_server death, setting the stage for bootanimation
+ // before DisplayManager takes over.
void initializeDisplays() REQUIRES(kMainThreadContext);
sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
@@ -1212,12 +1214,6 @@
float mGlobalSaturationFactor = 1.0f;
mat4 mClientColorMatrix;
- size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
- // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
- // this threshold, then begin logging.
- size_t mGraphicBufferProducerListSizeLogThreshold =
- static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
-
// protected by mStateLock (but we could use another lock)
bool mLayersRemoved = false;
bool mLayersAdded = false;
@@ -1552,6 +1548,7 @@
const sp<IScreenCaptureListener>&) override;
binder::Status captureLayers(const LayerCaptureArgs&,
const sp<IScreenCaptureListener>&) override;
+ binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults* results);
// TODO(b/239076119): Remove deprecated AIDL.
[[deprecated]] binder::Status clearAnimationFrameStats() override {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 6a155c1..7b5298c 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -25,6 +25,7 @@
#include "TransactionCallbackInvoker.h"
#include "BackgroundExecutor.h"
+#include "Utils/FenceUtils.h"
#include <cinttypes>
@@ -127,33 +128,8 @@
sp<IBinder> surfaceControl = handle->surfaceControl.promote();
if (surfaceControl) {
sp<Fence> prevFence = nullptr;
-
for (const auto& future : handle->previousReleaseFences) {
- sp<Fence> currentFence = future.get().value_or(Fence::NO_FENCE);
- if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) {
- prevFence = std::move(currentFence);
- } else if (prevFence != nullptr) {
- // If both fences are signaled or both are unsignaled, we need to merge
- // them to get an accurate timestamp.
- if (prevFence->getStatus() != Fence::Status::Invalid &&
- prevFence->getStatus() == currentFence->getStatus()) {
- char fenceName[32] = {};
- snprintf(fenceName, 32, "%.28s", handle->name.c_str());
- sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence);
- if (mergedFence->isValid()) {
- prevFence = std::move(mergedFence);
- }
- } else if (currentFence->getStatus() == Fence::Status::Unsignaled) {
- // If one fence has signaled and the other hasn't, the unsignaled
- // fence will approximately correspond with the correct timestamp.
- // There's a small race if both fences signal at about the same time
- // and their statuses are retrieved with unfortunate timing. However,
- // by this point, they will have both signaled and only the timestamp
- // will be slightly off; any dependencies after this point will
- // already have been met.
- prevFence = std::move(currentFence);
- }
- }
+ mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
}
handle->previousReleaseFence = prevFence;
handle->previousReleaseFences.clear();
diff --git a/services/surfaceflinger/Utils/FenceUtils.h b/services/surfaceflinger/Utils/FenceUtils.h
new file mode 100644
index 0000000..f6f7006
--- /dev/null
+++ b/services/surfaceflinger/Utils/FenceUtils.h
@@ -0,0 +1,51 @@
+/**
+ * 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 <ui/Fence.h>
+
+namespace android {
+
+// TODO: measure if Fence::merge is cheaper
+inline void mergeFence(const char* debugName, sp<Fence>&& incomingFence, sp<Fence>& prevFence) {
+ if (prevFence == nullptr && incomingFence->getStatus() != Fence::Status::Invalid) {
+ prevFence = std::move(incomingFence);
+ } else if (prevFence != nullptr) {
+ // If both fences are signaled or both are unsignaled, we need to merge
+ // them to get an accurate timestamp.
+ if (prevFence->getStatus() != Fence::Status::Invalid &&
+ prevFence->getStatus() == incomingFence->getStatus()) {
+ char fenceName[32] = {};
+ snprintf(fenceName, 32, "%.28s", debugName);
+ sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, incomingFence);
+ if (mergedFence->isValid()) {
+ prevFence = std::move(mergedFence);
+ }
+ } else if (incomingFence->getStatus() == Fence::Status::Unsignaled) {
+ // If one fence has signaled and the other hasn't, the unsignaled
+ // fence will approximately correspond with the correct timestamp.
+ // There's a small race if both fences signal at about the same time
+ // and their statuses are retrieved with unfortunate timing. However,
+ // by this point, they will have both signaled and only the timestamp
+ // will be slightly off; any dependencies after this point will
+ // already have been met.
+ prevFence = std::move(incomingFence);
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index a27e100..b07e7ac 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -128,6 +128,8 @@
DUMP_READ_ONLY_FLAG(fp16_client_target);
DUMP_READ_ONLY_FLAG(game_default_frame_rate);
DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
+ DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
+ DUMP_READ_ONLY_FLAG(vulkan_renderengine);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -202,6 +204,8 @@
FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
+FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
+FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index c01465b..2a30a40 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -68,6 +68,8 @@
bool fp16_client_target() const;
bool game_default_frame_rate() const;
bool enable_layer_command_batching() const;
+ bool screenshot_fence_preservation() const;
+ bool vulkan_renderengine() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 8a5500f..a6b12d0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -405,8 +405,8 @@
Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
android::mock::TimeStats timeStats;
- RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
- PowerMode::OFF);
+ RefreshRateStats refreshRateStats(timeStats,
+ Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
const auto fpsOpt = displayModes.get(modeId).transform(
[](const DisplayModePtr& mode) { return mode->getVsyncRate(); });
@@ -441,7 +441,9 @@
FuzzedDataProvider& fuzzer;
Period period() const { return getFuzzedDuration(fuzzer); }
- TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
+ TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const {
+ return getFuzzedTimePoint(fuzzer);
+ }
Period minFramePeriod() const { return period(); }
} vsyncSource{mFdp};
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index f097a13..fcbef01 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -166,3 +166,11 @@
bug: "293371537"
is_fixed_read_only: true
}
+
+flag {
+ name: "screenshot_fence_preservation"
+ namespace: "core_graphics"
+ description: "Bug fix around screenshot fences"
+ bug: "302703346"
+ is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
index f1bb231..b17b529 100644
--- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -17,7 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
-#include "DisplayTransactionTestHelpers.h"
+#include "DualDisplayTransactionTest.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -25,31 +25,10 @@
namespace android {
namespace {
-struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest {
- static constexpr bool kWithMockScheduler = false;
- ActiveDisplayRotationFlagsTest() : DisplayTransactionTest(kWithMockScheduler) {}
-
+struct ActiveDisplayRotationFlagsTest
+ : DualDisplayTransactionTest<hal::PowerMode::ON, hal::PowerMode::OFF> {
void SetUp() override {
- injectMockScheduler(kInnerDisplayId);
-
- // Inject inner and outer displays with uninitialized power modes.
- constexpr bool kInitPowerMode = false;
- {
- InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
- auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
- injector.setPowerMode(std::nullopt);
- injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
- mInnerDisplay = injector.inject();
- }
- {
- OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
- auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
- injector.setPowerMode(std::nullopt);
- mOuterDisplay = injector.inject();
- }
-
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ DualDisplayTransactionTest::SetUp();
// The flags are a static variable, so by modifying them in the test, we
// are modifying the real ones used by SurfaceFlinger. Save the original
@@ -64,10 +43,6 @@
void TearDown() override { mFlinger.mutableActiveDisplayRotationFlags() = mOldRotationFlags; }
- static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
- static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
-
- sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
ui::Transform::RotationFlags mOldRotationFlags;
};
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 99fef7e..387d2f2 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -347,7 +347,6 @@
// The HWC active configuration id
static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001;
- static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
test->mFlinger.mutablePendingHotplugEvents().emplace_back(
@@ -355,7 +354,7 @@
}
// Called by tests to inject a HWC display setup
- template <bool kInitPowerMode = true>
+ template <hal::PowerMode kPowerMode = hal::PowerMode::ON>
static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
const auto displayId = DisplayVariant::DISPLAY_ID::get();
ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -364,22 +363,37 @@
.setHwcDisplayId(HWC_DISPLAY_ID)
.setResolution(DisplayVariant::RESOLUTION)
.setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
+ .setPowerMode(kPowerMode)
.inject(&test->mFlinger, test->mComposer);
}
// Called by tests to inject a HWC display setup
- template <bool kInitPowerMode = true>
+ //
+ // TODO(b/241285876): The `kExpectSetPowerModeOnce` argument is set to `false` by tests that
+ // power on/off displays several times. Replace those catch-all expectations with `InSequence`
+ // and `RetiresOnSaturation`.
+ //
+ template <hal::PowerMode kPowerMode = hal::PowerMode::ON, bool kExpectSetPowerModeOnce = true>
static void injectHwcDisplay(DisplayTransactionTest* test) {
- if constexpr (kInitPowerMode) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
+ if constexpr (kExpectSetPowerModeOnce) {
+ if constexpr (kPowerMode == hal::PowerMode::ON) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, kPowerMode))
.WillOnce(Return(Error::NONE));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(Return(Error::NONE));
}
- injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
+
+ injectHwcDisplayWithNoDefaultCapabilities<kPowerMode>(test);
}
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -447,11 +461,9 @@
? IComposerClient::DisplayConnectionType::INTERNAL
: IComposerClient::DisplayConnectionType::EXTERNAL;
- using ::testing::AtLeast;
EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
- .Times(AtLeast(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(CONNECTION_TYPE),
- Return(hal::V2_4::Error::NONE)));
+ .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE),
+ Return(hal::V2_4::Error::NONE)));
}
EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
new file mode 100644
index 0000000..90e716f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 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 "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+template <hal::PowerMode kInnerDisplayPowerMode, hal::PowerMode kOuterDisplayPowerMode,
+ bool kExpectSetPowerModeOnce = true>
+struct DualDisplayTransactionTest : DisplayTransactionTest {
+ static constexpr bool kWithMockScheduler = false;
+ DualDisplayTransactionTest() : DisplayTransactionTest(kWithMockScheduler) {}
+
+ void SetUp() override {
+ injectMockScheduler(kInnerDisplayId);
+
+ {
+ InnerDisplayVariant::injectHwcDisplay<kInnerDisplayPowerMode, kExpectSetPowerModeOnce>(
+ this);
+
+ auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+ injector.setPowerMode(kInnerDisplayPowerMode);
+ mInnerDisplay = injector.inject();
+ }
+ {
+ OuterDisplayVariant::injectHwcDisplay<kOuterDisplayPowerMode, kExpectSetPowerModeOnce>(
+ this);
+
+ auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(kOuterDisplayPowerMode);
+ mOuterDisplay = injector.inject();
+ }
+ }
+
+ static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
+ static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+
+ sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index f695b09..9e8e306 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -24,7 +24,11 @@
#include <gtest/gtest.h>
#include <gui/LayerMetadata.h>
+#include "Client.h" // temporarily needed for LayerCreationArgs
#include "FpsReporter.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "fake/FakeClock.h"
@@ -76,7 +80,15 @@
sp<Layer> createBufferStateLayer(LayerMetadata metadata);
- TestableSurfaceFlinger mFlinger;
+ LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+ LayerMetadata metadata);
+
+ void createRootLayer(uint32_t id, LayerMetadata metadata);
+
+ void createLayer(uint32_t id, uint32_t parentId, LayerMetadata metadata);
+
+ frontend::LayerLifecycleManager mLifecycleManager;
+
mock::FrameTimeline mFrameTimeline =
mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
@@ -89,8 +101,8 @@
sp<TestableFpsListener> mFpsListener;
fake::FakeClock* mClock = new fake::FakeClock();
- sp<FpsReporter> mFpsReporter = sp<FpsReporter>::make(mFrameTimeline, *(mFlinger.flinger()),
- std::unique_ptr<Clock>(mClock));
+ sp<FpsReporter> mFpsReporter =
+ sp<FpsReporter>::make(mFrameTimeline, std::unique_ptr<Clock>(mClock));
};
FpsReporterTest::FpsReporterTest() {
@@ -98,9 +110,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.setupMockScheduler();
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-
mFpsListener = sp<TestableFpsListener>::make();
}
@@ -110,76 +119,94 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
+LayerCreationArgs FpsReporterTest::createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+ LayerMetadata metadata) {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
- return sp<Layer>::make(args);
+ LayerCreationArgs args(std::make_optional(id));
+ args.name = "testlayer";
+ args.addToRoot = canBeRoot;
+ args.flags = LAYER_FLAGS;
+ args.metadata = metadata;
+ args.parentId = parentId;
+ return args;
+}
+
+void FpsReporterTest::createRootLayer(uint32_t id, LayerMetadata metadata = LayerMetadata()) {
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<frontend::RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+ /*metadata=*/metadata)));
+ mLifecycleManager.addLayers(std::move(layers));
+}
+
+void FpsReporterTest::createLayer(uint32_t id, uint32_t parentId,
+ LayerMetadata metadata = LayerMetadata()) {
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<frontend::RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+ /*mirror=*/metadata)));
+ mLifecycleManager.addLayers(std::move(layers));
}
namespace {
TEST_F(FpsReporterTest, callsListeners) {
- mParent = createBufferStateLayer();
constexpr int32_t kTaskId = 12;
LayerMetadata targetMetadata;
targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId);
- mTarget = createBufferStateLayer(targetMetadata);
- mChild = createBufferStateLayer();
- mGrandChild = createBufferStateLayer();
- mUnrelated = createBufferStateLayer();
- mParent->addChild(mTarget);
- mTarget->addChild(mChild);
- mChild->addChild(mGrandChild);
- mParent->commitChildList();
- mFlinger.mutableCurrentState().layersSortedByZ.add(mParent);
- mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
- mFlinger.mutableCurrentState().layersSortedByZ.add(mChild);
- mFlinger.mutableCurrentState().layersSortedByZ.add(mGrandChild);
+
+ createRootLayer(1, targetMetadata);
+ createLayer(11, 1);
+ createLayer(111, 11);
+
+ frontend::LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
float expectedFps = 44.0;
- EXPECT_CALL(mFrameTimeline,
- computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(),
- mGrandChild->getSequence())))
+ EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(1, 11, 111)))
.WillOnce(Return(expectedFps));
mFpsReporter->addListener(mFpsListener, kTaskId);
mClock->advanceTime(600ms);
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy());
EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
mFpsReporter->removeListener(mFpsListener);
Mock::VerifyAndClearExpectations(&mFrameTimeline);
EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0);
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy());
}
TEST_F(FpsReporterTest, rateLimits) {
const constexpr int32_t kTaskId = 12;
LayerMetadata targetMetadata;
targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId);
- mTarget = createBufferStateLayer(targetMetadata);
- mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+ createRootLayer(1);
+ createLayer(11, 1, targetMetadata);
+
+ frontend::LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
float firstFps = 44.0;
float secondFps = 53.0;
- EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence())))
+ EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(11)))
.WillOnce(Return(firstFps))
.WillOnce(Return(secondFps));
mFpsReporter->addListener(mFpsListener, kTaskId);
mClock->advanceTime(600ms);
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy());
EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
mClock->advanceTime(200ms);
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy());
EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
mClock->advanceTime(200ms);
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy());
EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
mClock->advanceTime(200ms);
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy());
EXPECT_EQ(secondFps, mFpsListener->lastReportedFps);
}
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 6394d63..13b47ff 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -64,6 +64,18 @@
actualBuilder.update(args);
}
+ void update(LayerSnapshotBuilder& actualBuilder) {
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ update(actualBuilder, args);
+ }
+
void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges,
const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) {
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
@@ -1199,4 +1211,30 @@
EXPECT_EQ(getSnapshot(11)->dropInputMode, gui::DropInputMode::ALL);
}
+TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) {
+ LayerHierarchyTestBase::createRootLayer(3);
+ setColor(3, {-1._hf, -1._hf, -1._hf});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+ transactions.back().states.front().layerId = 3;
+ transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->token = sp<BBinder>::make();
+ mLifecycleManager.applyTransactions(transactions);
+
+ update(mSnapshotBuilder);
+
+ bool foundInputLayer = false;
+ mSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (snapshot.uniqueSequence == 3) {
+ foundInputLayer = true;
+ }
+ });
+ EXPECT_TRUE(foundInputLayer);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index e9c4d80..249ed40 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -72,7 +72,8 @@
struct MessageQueueTest : testing::Test {
void SetUp() override {
EXPECT_CALL(*mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration));
+ EXPECT_NO_FATAL_FAILURE(
+ mEventQueue.initVsyncInternal(mVSyncDispatch, mTokenManager, kDuration));
EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index e2e3d7b..fba77e9 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -37,7 +37,7 @@
~RefreshRateStatsTest();
void resetStats(Fps fps) {
- mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps, PowerMode::OFF);
+ mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps);
}
mock::TimeStats mTimeStats;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 82b4ad0..8b16a8a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -21,13 +21,9 @@
#include "mock/DisplayHardware/MockDisplayMode.h"
#include "mock/MockDisplayModeSpecs.h"
-#include <com_android_graphics_surfaceflinger_flags.h>
-#include <common/test/FlagUtils.h>
#include <ftl/fake_guard.h>
#include <scheduler/Fps.h>
-using namespace com::android::graphics::surfaceflinger;
-
namespace android {
namespace {
@@ -364,13 +360,6 @@
}
TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
- // For the inner display, this is handled by setupHwcHotplugCallExpectations.
- EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _))
- .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL),
- Return(hal::V2_4::Error::NONE)));
-
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -440,12 +429,6 @@
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
- // For the inner display, this is handled by setupHwcHotplugCallExpectations.
- EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _))
- .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL),
- Return(hal::V2_4::Error::NONE)));
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -529,13 +512,6 @@
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
- // For the inner display, this is handled by setupHwcHotplugCallExpectations.
- EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _))
- .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL),
- Return(hal::V2_4::Error::NONE)));
-
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index 844b96c..93c2829 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -17,7 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
-#include "DisplayTransactionTestHelpers.h"
+#include "DualDisplayTransactionTest.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -25,35 +25,9 @@
namespace android {
namespace {
-struct FoldableTest : DisplayTransactionTest {
- static constexpr bool kWithMockScheduler = false;
- FoldableTest() : DisplayTransactionTest(kWithMockScheduler) {}
-
- void SetUp() override {
- injectMockScheduler(kInnerDisplayId);
-
- // Inject inner and outer displays with uninitialized power modes.
- constexpr bool kInitPowerMode = false;
- {
- InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
- auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
- injector.setPowerMode(std::nullopt);
- injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
- mInnerDisplay = injector.inject();
- }
- {
- OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
- auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
- injector.setPowerMode(std::nullopt);
- mOuterDisplay = injector.inject();
- }
- }
-
- static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
- static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
-
- sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
-};
+constexpr bool kExpectSetPowerModeOnce = false;
+struct FoldableTest : DualDisplayTransactionTest<hal::PowerMode::OFF, hal::PowerMode::OFF,
+ kExpectSetPowerModeOnce> {};
TEST_F(FoldableTest, promotesPacesetterOnBoot) {
// When the device boots, the inner display should be the pacesetter.
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index 1583f64..eaf4684 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -17,66 +17,49 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
-#include "DisplayTransactionTestHelpers.h"
+#include "DualDisplayTransactionTest.h"
namespace android {
namespace {
-class InitializeDisplaysTest : public DisplayTransactionTest {};
+constexpr bool kExpectSetPowerModeOnce = false;
+struct InitializeDisplaysTest : DualDisplayTransactionTest<hal::PowerMode::OFF, hal::PowerMode::OFF,
+ kExpectSetPowerModeOnce> {};
-TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display is set up
- Case::Display::injectHwcDisplay(this);
- auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
- primaryDisplay.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect a call to get the active display config.
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
- // We expect a scheduled commit for the display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+TEST_F(InitializeDisplaysTest, initializesDisplays) {
+ // Scheduled by the display transaction, and by powering on each display.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3);
EXPECT_CALL(static_cast<mock::VSyncTracker&>(
mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
nextAnticipatedVSyncTimeFrom(_, _))
.WillRepeatedly(Return(0));
- // --------------------------------------------------------------------
- // Invocation
-
FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays());
- // --------------------------------------------------------------------
- // Postconditions
+ for (const auto& display : {mInnerDisplay, mOuterDisplay}) {
+ const auto token = display->getDisplayToken().promote();
+ ASSERT_TRUE(token);
- // The primary display should have a current state
- ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
- const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
+ ASSERT_TRUE(hasCurrentDisplayState(token));
+ const auto& state = getCurrentDisplayState(token);
- // The primary display state should be reset
- EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack);
- EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
+ const ui::LayerStack expectedLayerStack = display == mInnerDisplay
+ ? ui::DEFAULT_LAYER_STACK
+ : ui::LayerStack::fromValue(ui::DEFAULT_LAYER_STACK.id + 1);
- // The width and height should both be zero
- EXPECT_EQ(0u, primaryDisplayState.width);
- EXPECT_EQ(0u, primaryDisplayState.height);
+ EXPECT_EQ(expectedLayerStack, state.layerStack);
+ EXPECT_EQ(ui::ROTATION_0, state.orientation);
+ EXPECT_EQ(Rect::INVALID_RECT, state.orientedDisplaySpaceRect);
+ EXPECT_EQ(Rect::INVALID_RECT, state.layerStackSpaceRect);
- // The display should be set to PowerMode::ON
- ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
- auto displayDevice = primaryDisplay.mutableDisplayDevice();
- EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
+ EXPECT_EQ(0u, state.width);
+ EXPECT_EQ(0u, state.height);
- // The display transaction needed flag should be set.
+ ASSERT_TRUE(hasDisplayDevice(token));
+ EXPECT_EQ(PowerMode::ON, getDisplayDevice(token).getPowerMode());
+ }
+
EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f00eacc..9ef0749 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -268,7 +268,7 @@
vsyncTrackerCallback);
}
- mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
+ mScheduler->initVsync(*mTokenManager, 0ms);
mScheduler->mutableAppConnectionHandle() =
mScheduler->createConnection(std::move(appEventThread));
@@ -750,7 +750,6 @@
static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
- static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON;
FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
bool isPrimary)
@@ -793,7 +792,7 @@
return *this;
}
- auto& setPowerMode(std::optional<hal::PowerMode> mode) {
+ auto& setPowerMode(hal::PowerMode mode) {
mPowerMode = mode;
return *this;
}
@@ -817,9 +816,7 @@
mHwcDisplayType);
display->mutableIsConnected() = true;
- if (mPowerMode) {
- display->setPowerMode(*mPowerMode);
- }
+ display->setPowerMode(mPowerMode);
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
@@ -885,7 +882,7 @@
int32_t mDpiY = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
- std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
+ hal::PowerMode mPowerMode = hal::PowerMode::ON;
const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
mCapabilities = nullptr;
};
@@ -962,7 +959,7 @@
return *this;
}
- auto& setPowerMode(std::optional<hal::PowerMode> mode) {
+ auto& setPowerMode(hal::PowerMode mode) {
mCreationArgs.initialPowerMode = mode;
return *this;
}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index b17c8ad..ae41e7e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -34,8 +34,6 @@
namespace android::Hwc2::mock {
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::Mode;
using android::power::HalResult;
class MockPowerHalController : public power::PowerHalController {
@@ -43,12 +41,22 @@
MockPowerHalController();
~MockPowerHalController() override;
MOCK_METHOD(void, init, (), (override));
- MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override));
- MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override));
+ 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>>,
createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t),
(override));
+ MOCK_METHOD(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));
MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override));
+ MOCK_METHOD(HalResult<aidl::android::hardware::power::ChannelConfig>, getSessionChannel,
+ (int tgid, int uid), (override));
+ MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override));
};
} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 6b3c379..867fc45 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1369,197 +1369,221 @@
const VkPhysicalDevice& pdev = GetData(device).driver_physical_device;
const InstanceData& instance_data = GetData(pdev);
const InstanceDriverTable& instance_dispatch = instance_data.driver;
- if (!instance_dispatch.GetPhysicalDeviceImageFormatProperties2 &&
- !instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
- uint64_t native_usage = 0;
- void* usage_info_pNext = nullptr;
- VkResult result;
+ if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2 ||
+ instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
+ // Look through the create_info pNext chain passed to createSwapchainKHR
+ // for an image compression control struct.
+ // if one is found AND the appropriate extensions are enabled, create a
+ // VkImageCompressionControlEXT structure to pass on to
+ // GetPhysicalDeviceImageFormatProperties2
+ void* compression_control_pNext = nullptr;
VkImageCompressionControlEXT image_compression = {};
- const auto& dispatch = GetData(device).driver;
- if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
- VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
- gralloc_usage_info.sType =
- VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
- gralloc_usage_info.format = create_info->imageFormat;
- gralloc_usage_info.imageUsage = create_info->imageUsage;
- gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
-
- // Look through the pNext chain for an image compression control struct
- // if one is found AND the appropriate extensions are enabled,
- // append it to be the gralloc usage pNext chain
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
- create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(
- create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- usage_info_pNext = &image_compression;
- } break;
-
- default:
- // Ignore all other info structs
- break;
- }
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ compression_control_pNext = &image_compression;
+ } break;
+ default:
+ // Ignore all other info structs
+ break;
}
- gralloc_usage_info.pNext = usage_info_pNext;
-
- result = dispatch.GetSwapchainGrallocUsage4ANDROID(
- device, &gralloc_usage_info, &native_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
- VkGrallocUsageInfoANDROID gralloc_usage_info = {};
- gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
- gralloc_usage_info.format = create_info->imageFormat;
- gralloc_usage_info.imageUsage = create_info->imageUsage;
-
- // Look through the pNext chain for an image compression control struct
- // if one is found AND the appropriate extensions are enabled,
- // append it to be the gralloc usage pNext chain
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
- create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(
- create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- usage_info_pNext = &image_compression;
- } break;
-
- default:
- // Ignore all other info structs
- break;
- }
- }
- gralloc_usage_info.pNext = usage_info_pNext;
-
- result = dispatch.GetSwapchainGrallocUsage3ANDROID(
- device, &gralloc_usage_info, &native_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
- uint64_t consumer_usage, producer_usage;
- ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
- result = dispatch.GetSwapchainGrallocUsage2ANDROID(
- device, create_info->imageFormat, create_info->imageUsage,
- swapchain_image_usage, &consumer_usage, &producer_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- native_usage =
- convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
- } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
- int32_t legacy_usage = 0;
- result = dispatch.GetSwapchainGrallocUsageANDROID(
- device, create_info->imageFormat, create_info->imageUsage,
- &legacy_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- native_usage = static_cast<uint64_t>(legacy_usage);
}
- *producer_usage = native_usage;
- return VK_SUCCESS;
+ // call GetPhysicalDeviceImageFormatProperties2KHR
+ VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
+ .pNext = compression_control_pNext,
+ .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+ };
+
+ // AHB does not have an sRGB format so we can't pass it to GPDIFP
+ // We need to convert the format to unorm if it is srgb
+ VkFormat format = create_info->imageFormat;
+ if (format == VK_FORMAT_R8G8B8A8_SRGB) {
+ format = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+
+ VkPhysicalDeviceImageFormatInfo2 image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+ .pNext = &external_image_format_info,
+ .format = format,
+ .type = VK_IMAGE_TYPE_2D,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = create_info->imageUsage,
+ .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+ };
+
+ VkAndroidHardwareBufferUsageANDROID ahb_usage;
+ ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
+ ahb_usage.pNext = nullptr;
+
+ VkImageFormatProperties2 image_format_properties;
+ image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+ image_format_properties.pNext = &ahb_usage;
+
+ if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) {
+ VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2(
+ pdev, &image_format_info, &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ }
+ else {
+ VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR(
+ pdev, &image_format_info,
+ &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d",
+ result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ }
+
+ // Determine if USAGE_FRONT_BUFFER is needed.
+ // GPDIFP2 has no means of using VkSwapchainImageUsageFlagsANDROID when
+ // querying for producer_usage. So androidHardwareBufferUsage will not
+ // contain USAGE_FRONT_BUFFER. We need to manually check for usage here.
+ if (!(swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID)) {
+ *producer_usage = ahb_usage.androidHardwareBufferUsage;
+ return VK_SUCCESS;
+ }
+
+ // Check if USAGE_FRONT_BUFFER is supported for this swapchain
+ AHardwareBuffer_Desc ahb_desc = {
+ .width = create_info->imageExtent.width,
+ .height = create_info->imageExtent.height,
+ .layers = create_info->imageArrayLayers,
+ .format = create_info->imageFormat,
+ .usage = ahb_usage.androidHardwareBufferUsage | AHARDWAREBUFFER_USAGE_FRONT_BUFFER,
+ .stride = 0, // stride is always ignored when calling isSupported()
+ };
+
+ // If FRONT_BUFFER is not supported,
+ // then we need to call GetSwapchainGrallocUsageXAndroid below
+ if (AHardwareBuffer_isSupported(&ahb_desc)) {
+ *producer_usage = ahb_usage.androidHardwareBufferUsage;
+ *producer_usage |= AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+ return VK_SUCCESS;
+ }
}
- // Look through the create_info pNext chain passed to createSwapchainKHR
- // for an image compression control struct.
- // if one is found AND the appropriate extensions are enabled, create a
- // VkImageCompressionControlEXT structure to pass on to GetPhysicalDeviceImageFormatProperties2
- void* compression_control_pNext = nullptr;
+ uint64_t native_usage = 0;
+ void* usage_info_pNext = nullptr;
+ VkResult result;
VkImageCompressionControlEXT image_compression = {};
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- compression_control_pNext = &image_compression;
- } break;
- default:
- // Ignore all other info structs
- break;
+ const auto& dispatch = GetData(device).driver;
+ if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+ VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType =
+ VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+ gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
}
- }
+ gralloc_usage_info.pNext = usage_info_pNext;
- // call GetPhysicalDeviceImageFormatProperties2KHR
- VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
- .pNext = compression_control_pNext,
- .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
- };
-
- // AHB does not have an sRGB format so we can't pass it to GPDIFP
- // We need to convert the format to unorm if it is srgb
- VkFormat format = create_info->imageFormat;
- if (format == VK_FORMAT_R8G8B8A8_SRGB) {
- format = VK_FORMAT_R8G8B8A8_UNORM;
- }
-
- VkPhysicalDeviceImageFormatInfo2 image_format_info = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
- .pNext = &external_image_format_info,
- .format = format,
- .type = VK_IMAGE_TYPE_2D,
- .tiling = VK_IMAGE_TILING_OPTIMAL,
- .usage = create_info->imageUsage,
- .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
- };
-
- VkAndroidHardwareBufferUsageANDROID ahb_usage;
- ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
- ahb_usage.pNext = nullptr;
-
- VkImageFormatProperties2 image_format_properties;
- image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
- image_format_properties.pNext = &ahb_usage;
-
- if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) {
- VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2(
- pdev, &image_format_info, &image_format_properties);
+ result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
if (result != VK_SUCCESS) {
- ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result);
+ ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
return VK_ERROR_SURFACE_LOST_KHR;
}
- }
- else {
- VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR(
- pdev, &image_format_info,
- &image_format_properties);
+ } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+ VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
if (result != VK_SUCCESS) {
- ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d",
- result);
+ ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
return VK_ERROR_SURFACE_LOST_KHR;
}
+ } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ uint64_t consumer_usage, producer_usage;
+ ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
+ result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ swapchain_image_usage, &consumer_usage, &producer_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ native_usage =
+ convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
+ } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
+ int32_t legacy_usage = 0;
+ result = dispatch.GetSwapchainGrallocUsageANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ &legacy_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ native_usage = static_cast<uint64_t>(legacy_usage);
}
-
- *producer_usage = ahb_usage.androidHardwareBufferUsage;
+ *producer_usage = native_usage;
return VK_SUCCESS;
}