Merge "SF: Remove a redundant word from comment." into main am: 8389f57b45
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/3388839
Change-Id: I08adde3740922c770baa13d407164720f5915be8
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index ba55082..a689b2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -38,7 +38,13 @@
cc_library_headers {
name: "native_headers",
+ vendor_available: true,
host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
export_include_dirs: [
"include/",
],
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2ce3fb0..df1ef29 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -24,6 +24,7 @@
libs/nativewindow/
libs/renderengine/
libs/ui/
+ libs/vibrator/
libs/vr/
opengl/libs/
services/bufferhub/
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index add9f92..5e83e33 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1560,6 +1560,13 @@
CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
printf("========================================================\n");
+ printf("== Networking Policy\n");
+ printf("========================================================\n");
+
+ RunDumpsys("DUMPSYS NETWORK POLICY", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+
+ printf("========================================================\n");
printf("== Dropbox crashes\n");
printf("========================================================\n");
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs
index db3fd77..e91e5da 100644
--- a/cmds/evemu-record/main.rs
+++ b/cmds/evemu-record/main.rs
@@ -50,8 +50,10 @@
/// The first event received from the device.
FirstEvent,
- /// The time when the system booted.
- Boot,
+ /// The Unix epoch (00:00:00 UTC on 1st January 1970), so that all timestamps are Unix
+ /// timestamps. This makes the events in the recording easier to match up with those from other
+ /// log sources.
+ Epoch,
}
fn get_choice(max: u32) -> u32 {
@@ -188,7 +190,7 @@
//
// [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1),
- TimestampBase::Boot => TimeVal::new(0, 0),
+ TimestampBase::Epoch => TimeVal::new(0, 0),
};
print_event(output, &event.offset_time_by(start_time))?;
loop {
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index c163095..77e7328 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -18,6 +18,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayMode.h>
@@ -202,6 +203,14 @@
bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
sp<GLConsumer>* glConsumer, EGLSurface* surface) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<GLConsumer> glc = new GLConsumer(name, GL_TEXTURE_EXTERNAL_OES, false, true);
+ glc->setDefaultBufferSize(w, h);
+ glc->getSurface()->setMaxDequeuedBufferCount(2);
+ glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+
+ sp<ANativeWindow> anw = glc->getSurface();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -212,6 +221,7 @@
glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
sp<ANativeWindow> anw = new Surface(producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr);
if (s == EGL_NO_SURFACE) {
fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index c18d3f5..50c2cd8 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -24,7 +24,7 @@
cc_defaults {
name: "idlcli-defaults",
shared_libs: [
- "android.hardware.vibrator-V2-ndk",
+ "android.hardware.vibrator-V3-ndk",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index e100eac..b943495 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -49,7 +49,7 @@
template <typename I>
inline auto getService(std::string name) {
const auto instance = std::string() + I::descriptor + "/" + name;
- auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+ auto vibBinder = ndk::SpAIBinder(AServiceManager_checkService(instance.c_str()));
return I::fromBinder(vibBinder);
}
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index c3f2fed..f593eda 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -35,7 +35,11 @@
The jerk thresholds are based on normalized dt = 1 calculations.
-->
- <low-jerk>1.0</low-jerk>
- <high-jerk>1.1</high-jerk>
+ <low-jerk>1.5</low-jerk>
+ <high-jerk>2.0</high-jerk>
+
+ <!-- The alpha in the first-order IIR filter for jerk smoothing. An alpha
+ of 1 results in no smoothing.-->
+ <jerk-alpha>0.25</jerk-alpha>
</motion-predictor>
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 20c5d94..3486e9b 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -84,7 +84,6 @@
/**
* An opaque type representing a handle to a performance hint manager.
- * It must be released after use.
*
* To use:<ul>
* <li>Obtain the performance hint manager instance by calling
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 82cacca..bf9acb3 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -145,6 +145,9 @@
* Buffers which are replaced or removed from the scene in the transaction invoking
* this callback may be reused after this point.
*
+ * Starting with API level 36, prefer using \a ASurfaceTransaction_OnBufferRelease to listen
+ * to when a buffer is ready to be reused.
+ *
* \param context Optional context provided by the client that is passed into
* the callback.
*
@@ -157,8 +160,7 @@
* Available since API level 29.
*/
typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context,
- ASurfaceTransactionStats* _Nonnull stats)
- __INTRODUCED_IN(29);
+ ASurfaceTransactionStats* _Nonnull stats);
/**
* The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
@@ -186,8 +188,36 @@
* Available since API level 31.
*/
typedef void (*ASurfaceTransaction_OnCommit)(void* _Null_unspecified context,
- ASurfaceTransactionStats* _Nonnull stats)
- __INTRODUCED_IN(31);
+ ASurfaceTransactionStats* _Nonnull stats);
+
+/**
+ * The ASurfaceTransaction_OnBufferRelease callback is invoked when a buffer that was passed in
+ * ASurfaceTransaction_setBuffer is ready to be reused.
+ *
+ * This callback is guaranteed to be invoked if ASurfaceTransaction_setBuffer is called with a non
+ * null buffer. If the buffer in the transaction is replaced via another call to
+ * ASurfaceTransaction_setBuffer, the callback will be invoked immediately. Otherwise the callback
+ * will be invoked before the ASurfaceTransaction_OnComplete callback after the buffer was
+ * presented.
+ *
+ * If this callback is set, caller should not release the buffer using the
+ * ASurfaceTransaction_OnComplete.
+ *
+ * \param context Optional context provided by the client that is passed into the callback.
+ *
+ * \param release_fence_fd Returns the fence file descriptor used to signal the release of buffer
+ * associated with this callback. If this fence is valid (>=0), the buffer has not yet been released
+ * and the fence will signal when the buffer has been released. If the fence is -1 , the buffer is
+ * already released. The recipient of the callback takes ownership of the fence fd and is
+ * responsible for closing it.
+ *
+ * THREADING
+ * The callback can be invoked on any thread.
+ *
+ * Available since API level 36.
+ */
+typedef void (*ASurfaceTransaction_OnBufferRelease)(void* _Null_unspecified context,
+ int release_fence_fd);
/**
* Returns the timestamp of when the frame was latched by the framework. Once a frame is
@@ -251,7 +281,7 @@
/**
* The returns the fence used to signal the release of the PREVIOUS buffer set on
* this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the
- * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS
+ * fence will signal when the PREVIOUS buffer has been released. If the fence is -1, the PREVIOUS
* buffer is already released. The recipient of the callback takes ownership of the
* previousReleaseFenceFd and is responsible for closing it.
*
@@ -353,6 +383,9 @@
* Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
* as the surface control might be composited using the GPU.
*
+ * Starting with API level 36, prefer using \a ASurfaceTransaction_setBufferWithRelease to
+ * set a buffer and a callback which will be invoked when the buffer is ready to be reused.
+ *
* Available since API level 29.
*/
void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction,
@@ -361,6 +394,29 @@
__INTRODUCED_IN(29);
/**
+ * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the
+ * acquire_fence_fd should be a file descriptor that is signaled when all pending work
+ * for the buffer is complete and the buffer can be safely read.
+ *
+ * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
+ * for closing it.
+ *
+ * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
+ * as the surface control might be composited using the GPU.
+ *
+ * When the buffer is ready to be reused, the ASurfaceTransaction_OnBufferRelease
+ * callback will be invoked. If the buffer is null, the callback will not be invoked.
+ *
+ * Available since API level 36.
+ */
+void ASurfaceTransaction_setBufferWithRelease(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ AHardwareBuffer* _Nonnull buffer,
+ int acquire_fence_fd, void* _Null_unspecified context,
+ ASurfaceTransaction_OnBufferRelease _Nonnull func)
+ __INTRODUCED_IN(36);
+
+/**
* Updates the color for \a surface_control. This will make the background color for the
* ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g,
* and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 769670e..0b7e16b 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -27,7 +27,7 @@
namespace android {
// ----------------------------------------------------------------------------
-
+// TODO(b/309532236) replace this class with AIDL generated parcelable
class IAudioManager : public IInterface
{
public:
@@ -43,6 +43,7 @@
RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6,
PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 7,
PORT_EVENT = IBinder::FIRST_CALL_TRANSACTION + 8,
+ PERMISSION_UPDATE_BARRIER = IBinder::FIRST_CALL_TRANSACTION + 9,
};
DECLARE_META_INTERFACE(AudioManager)
@@ -63,6 +64,7 @@
/*oneway*/ virtual status_t playerSessionId(audio_unique_id_t piid, audio_session_t sessionId) = 0;
/*oneway*/ virtual status_t portEvent(audio_port_handle_t portId, player_state_t event,
const std::unique_ptr<os::PersistableBundle>& extras) = 0;
+ virtual status_t permissionUpdateBarrier() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/input/Input.h b/include/input/Input.h
index ec08cdd..a8684bd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -25,6 +25,7 @@
#include <android/input.h>
#ifdef __linux__
#include <android/os/IInputConstants.h>
+#include <android/os/MotionEventFlag.h>
#endif
#include <android/os/PointerIconType.h>
#include <math.h>
@@ -69,15 +70,17 @@
* actual intent.
*/
AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
- android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ static_cast<int32_t>(android::os::MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED),
+
AMOTION_EVENT_FLAG_HOVER_EXIT_PENDING =
- android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING,
+ static_cast<int32_t>(android::os::MotionEventFlag::HOVER_EXIT_PENDING),
+
/**
* This flag indicates that the event has been generated by a gesture generator. It
* provides a hint to the GestureDetector to not apply any touch slop.
*/
AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE =
- android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE,
+ static_cast<int32_t>(android::os::MotionEventFlag::IS_GENERATED_GESTURE),
/**
* This flag indicates that the event will not cause a focus change if it is directed to an
@@ -86,27 +89,27 @@
* into focus.
*/
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE =
- android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE,
+ static_cast<int32_t>(android::os::MotionEventFlag::NO_FOCUS_CHANGE),
/**
* This event was generated or modified by accessibility service.
*/
AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
- android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+ static_cast<int32_t>(android::os::MotionEventFlag::IS_ACCESSIBILITY_EVENT),
AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS =
- android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS,
+ static_cast<int32_t>(android::os::MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS),
/* Motion event is inconsistent with previously sent motion events. */
- AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
+ AMOTION_EVENT_FLAG_TAINTED = static_cast<int32_t>(android::os::MotionEventFlag::TAINTED),
/** Private flag, not used in Java. */
AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION =
- android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION,
+ static_cast<int32_t>(android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION),
/** Private flag, not used in Java. */
- AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants::
- MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = static_cast<int32_t>(
+ android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION),
/** Mask for all private flags that are not used in Java. */
AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
@@ -193,6 +196,13 @@
#define MAX_POINTER_ID 31
/*
+ * Number of high resolution scroll units for one detent (scroll wheel click), as defined in
+ * evdev. This is relevant when an input device is emitting REL_WHEEL_HI_RES or REL_HWHEEL_HI_RES
+ * events.
+ */
+constexpr int32_t kEvdevHighResScrollUnitsPerDetent = 120;
+
+/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
struct AInputEvent {
@@ -241,6 +251,8 @@
TOUCH_MODE = AINPUT_EVENT_TYPE_TOUCH_MODE,
ftl_first = KEY,
ftl_last = TOUCH_MODE,
+ // Used by LatencyTracker fuzzer
+ kMaxValue = ftl_last
};
std::string inputEventSourceToString(int32_t source);
@@ -890,9 +902,7 @@
void splitFrom(const MotionEvent& other, std::bitset<MAX_POINTER_ID + 1> splitPointerIds,
int32_t newEventId);
- void addSample(
- nsecs_t eventTime,
- const PointerCoords* pointerCoords);
+ void addSample(nsecs_t eventTime, const PointerCoords* pointerCoords, int32_t eventId);
void offsetLocation(float xOffset, float yOffset);
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 9e48b08..c98b9cf 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -16,15 +16,21 @@
#pragma once
+#include <map>
+#include <memory>
+#include <optional>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/Resampler.h>
#include <utils/Looper.h>
-#include "InputTransport.h"
namespace android {
/**
* An interface to receive batched input events. Even if you don't want batching, you still have to
* use this interface, and some of the events will be batched if your implementation is slow to
- * handle the incoming input.
+ * handle the incoming input. The events received by these callbacks are never null.
*/
class InputConsumerCallbacks {
public:
@@ -34,7 +40,7 @@
/**
* When you receive this callback, you must (eventually) call "consumeBatchedInputEvents".
* If you don't want batching, then call "consumeBatchedInputEvents" immediately with
- * std::nullopt frameTime to receive the pending motion event(s).
+ * std::nullopt requestedFrameTime to receive the pending motion event(s).
* @param pendingBatchSource the source of the pending batch.
*/
virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0;
@@ -47,13 +53,13 @@
/**
* Consumes input events from an input channel.
*
- * This is a re-implementation of InputConsumer that does not have resampling at the current moment.
- * A lot of the higher-level logic has been folded into this class, to make it easier to use.
- * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer,
- * as well as various actions like adding the fd to the Choreographer.
+ * This is a re-implementation of InputConsumer. At the moment it only supports resampling for
+ * single pointer events. A lot of the higher-level logic has been folded into this class, to make
+ * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled
+ * in the jni layer, as well as various actions like adding the fd to the Choreographer.
*
* TODO(b/297226446): use this instead of "InputConsumer":
- * - Add resampling to this class
+ * - Add resampling for multiple pointer events.
* - Allow various resampling strategies to be specified
* - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer".
* - Add tracing
@@ -64,8 +70,18 @@
*/
class InputConsumerNoResampling final {
public:
+ /**
+ * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever
+ * the event is ready to consume.
+ * @param looper needs to be sp and not shared_ptr because it inherits from
+ * RefBase
+ * @param resampler the resampling strategy to use. If null, no resampling will be
+ * performed.
+ */
explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper, InputConsumerCallbacks& callbacks);
+ sp<Looper> looper, InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler);
+
~InputConsumerNoResampling();
/**
@@ -74,15 +90,17 @@
void finishInputEvent(uint32_t seq, bool handled);
void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
/**
- * If you want to consume all events immediately (disable batching), the you still must call
- * this. For frameTime, use a std::nullopt.
- * @param frameTime the time up to which consume the events. When there's double (or triple)
- * buffering, you may want to not consume all events currently available, because you could be
- * still working on an older frame, but there could already have been events that arrived that
- * are more recent.
+ * If you want to consume all events immediately (disable batching), then you still must call
+ * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption
+ * will occur at requestedFrameTime. The resampling strategy may modify it.
+ * @param requestedFrameTime the time up to which consume the events. When there's double (or
+ * triple) buffering, you may want to not consume all events currently available, because you
+ * could be still working on an older frame, but there could already have been events that
+ * arrived that are more recent.
* @return whether any events were actually consumed
*/
- bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime);
+ bool consumeBatchedInputEvents(std::optional<nsecs_t> requestedFrameTime);
+
/**
* Returns true when there is *likely* a pending batch or a pending event in the channel.
*
@@ -99,6 +117,7 @@
std::shared_ptr<InputChannel> mChannel;
sp<Looper> mLooper;
InputConsumerCallbacks& mCallbacks;
+ std::unique_ptr<Resampler> mResampler;
// Looper-related infrastructure
/**
@@ -177,11 +196,34 @@
/**
* Batched InputMessages, per deviceId.
* For each device, we are storing a queue of batched messages. These will all be collapsed into
- * a single MotionEvent (up to a specific frameTime) when the consumer calls
+ * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls
* `consumeBatchedInputEvents`.
*/
std::map<DeviceId, std::queue<InputMessage>> mBatches;
/**
+ * Creates a MotionEvent by consuming samples from the provided queue. If one message has
+ * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is
+ * assumed that messages are queued in chronological order. In other words, only events that
+ * occurred prior to the adjustedFrameTime will be consumed.
+ * @param requestedFrameTime the time up to which to consume events.
+ * @param messages the queue of messages to consume from
+ */
+ std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent(
+ const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages);
+
+ /**
+ * Consumes the batched input events, optionally allowing the caller to specify a device id
+ * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at
+ * requestedFrameTime.
+ * @param deviceId The device id from which to consume events. If std::nullopt, consumes events
+ * from any device id.
+ * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes
+ * input events with any timestamp.
+ * @return Whether or not any events were consumed.
+ */
+ bool consumeBatchedInputEvents(std::optional<DeviceId> deviceId,
+ std::optional<nsecs_t> requestedFrameTime);
+ /**
* A map from a single sequence number to several sequence numbers. This is needed because of
* batching. When batching is enabled, a single MotionEvent will contain several samples. Each
* sample came from an individual InputMessage of Type::Motion, and therefore will have to be
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7d8c19e..1a48239 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -389,6 +389,7 @@
CONFIGURATION = 0, /* .idc file */
KEY_LAYOUT = 1, /* .kl file */
KEY_CHARACTER_MAP = 2, /* .kcm file */
+ ftl_last = KEY_CHARACTER_MAP,
};
/*
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 25d35e9..5bd5070 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -19,6 +19,8 @@
#include <android/input.h>
#include <attestation/HmacKeyManager.h>
#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <ui/LogicalDisplayId.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
#include <vector>
@@ -44,6 +46,11 @@
PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+ PointerBuilder& isResampled(bool isResampled) {
+ mCoords.isResampled = isResampled;
+ return *this;
+ }
+
PointerBuilder& axis(int32_t axis, float value) {
mCoords.setAxisValue(axis, value);
return *this;
@@ -58,6 +65,87 @@
PointerCoords mCoords;
};
+class InputMessageBuilder {
+public:
+ InputMessageBuilder(InputMessage::Type type, uint32_t seq) : mType{type}, mSeq{seq} {}
+
+ InputMessageBuilder& eventId(int32_t eventId) {
+ mEventId = eventId;
+ return *this;
+ }
+
+ InputMessageBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ InputMessageBuilder& deviceId(DeviceId deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ InputMessageBuilder& source(int32_t source) {
+ mSource = source;
+ return *this;
+ }
+
+ InputMessageBuilder& displayId(ui::LogicalDisplayId displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ InputMessageBuilder& action(int32_t action) {
+ mAction = action;
+ return *this;
+ }
+
+ InputMessageBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ InputMessageBuilder& pointer(PointerBuilder pointerBuilder) {
+ mPointers.push_back(pointerBuilder);
+ return *this;
+ }
+
+ InputMessage build() const {
+ InputMessage message{};
+ // Header
+ message.header.type = mType;
+ message.header.seq = mSeq;
+ // Body
+ message.body.motion.eventId = mEventId;
+ message.body.motion.pointerCount = mPointers.size();
+ message.body.motion.eventTime = mEventTime;
+ message.body.motion.deviceId = mDeviceId;
+ message.body.motion.source = mSource;
+ message.body.motion.displayId = mDisplayId.val();
+ message.body.motion.action = mAction;
+ message.body.motion.downTime = mDownTime;
+
+ for (size_t i = 0; i < mPointers.size(); ++i) {
+ message.body.motion.pointers[i].properties = mPointers[i].buildProperties();
+ message.body.motion.pointers[i].coords = mPointers[i].buildCoords();
+ }
+ return message;
+ }
+
+private:
+ const InputMessage::Type mType;
+ const uint32_t mSeq;
+
+ int32_t mEventId{InputEvent::nextId()};
+ nsecs_t mEventTime{systemTime(SYSTEM_TIME_MONOTONIC)};
+ DeviceId mDeviceId{DEFAULT_DEVICE_ID};
+ int32_t mSource{AINPUT_SOURCE_TOUCHSCREEN};
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
+ int32_t mAction{AMOTION_EVENT_ACTION_MOVE};
+ nsecs_t mDownTime{mEventTime};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
class MotionEventBuilder {
public:
MotionEventBuilder(int32_t action, int32_t source) {
@@ -127,7 +215,7 @@
return *this;
}
- MotionEvent build() {
+ MotionEvent build() const {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
for (const PointerBuilder& pointer : mPointers) {
@@ -135,20 +223,22 @@
pointerCoords.push_back(pointer.buildCoords());
}
+ auto [xCursorPosition, yCursorPosition] =
+ std::make_pair(mRawXCursorPosition, mRawYCursorPosition);
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
+ !MotionEvent::isValidCursorPosition(xCursorPosition, yCursorPosition)) {
+ xCursorPosition = pointerCoords[0].getX();
+ yCursorPosition = pointerCoords[0].getY();
}
MotionEvent event;
event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
MotionClassification::NONE, mTransform,
- /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, mRawTransform, mDownTime, mEventTime,
- mPointers.size(), pointerProperties.data(), pointerCoords.data());
+ /*xPrecision=*/0, /*yPrecision=*/0, xCursorPosition, yCursorPosition,
+ mRawTransform, mDownTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
return event;
}
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index b26a194..0cd8720 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -263,7 +263,7 @@
* Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- status_t sendMessage(const InputMessage* msg);
+ virtual status_t sendMessage(const InputMessage* msg);
/* Receive a message sent by the other endpoint.
*
@@ -275,14 +275,14 @@
* Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- android::base::Result<InputMessage> receiveMessage();
+ virtual android::base::Result<InputMessage> receiveMessage();
/* Tells whether there is a message in the channel available to be received.
*
* This is only a performance hint and may return false negative results. Clients should not
* rely on availability of the message based on the return value.
*/
- bool probablyHasInput() const;
+ virtual bool probablyHasInput() const;
/* Wait until there is a message in the channel.
*
@@ -323,11 +323,12 @@
*/
sp<IBinder> getConnectionToken() const;
+protected:
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
+
private:
static std::unique_ptr<InputChannel> create(const std::string& name,
android::base::unique_fd fd, sp<IBinder> token);
-
- InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
};
/*
@@ -363,7 +364,8 @@
* Returns OK on success.
* Returns WOULD_BLOCK if the channel is full.
* Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
+ * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS,
+ * or if the verifier is enabled and the event failed verification upon publishing.
* Other errors probably indicate that the channel is broken.
*/
status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 92d5ec4..67b37b1 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -126,9 +126,9 @@
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
Vector<KeyEvent>& outEvents) const;
- /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
- * and usageCodes are mapped to corresponding Android Keycode */
- void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+ /* Maps some Android key code to another Android key code. This mapping is applied after
+ * scanCode and usageCodes are mapped to corresponding Android Keycode */
+ void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping);
/* Maps a scan code and usage code to a key code, in case this key map overrides
* the mapping in some way. */
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index f715039..200c301 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -43,7 +43,9 @@
class JerkTracker {
public:
// Initialize the tracker. If normalizedDt is true, assume that each sample pushed has dt=1.
- JerkTracker(bool normalizedDt);
+ // alpha is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
+ // in no smoothing.
+ JerkTracker(bool normalizedDt, float alpha);
// Add a position to the tracker and update derivative estimates.
void pushSample(int64_t timestamp, float xPos, float yPos);
@@ -58,10 +60,13 @@
private:
const bool mNormalizedDt;
+ // Coefficient of first-order IIR filter to smooth jerk calculation.
+ const float mAlpha;
RingBuffer<int64_t> mTimestamps{4};
std::array<float, 4> mXDerivatives{}; // [x, x', x'', x''']
std::array<float, 4> mYDerivatives{}; // [y, y', y'', y''']
+ float mJerkMagnitude;
};
/**
@@ -124,15 +129,17 @@
std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
std::optional<MotionEvent> mLastEvent;
- // mJerkTracker assumes normalized dt = 1 between recorded samples because
- // the underlying mModel input also assumes fixed-interval samples.
- // Normalized dt as 1 is also used to correspond with the similar Jank
- // implementation from the JetPack MotionPredictor implementation.
- JerkTracker mJerkTracker{true};
- std::optional<MotionPredictorMetricsManager> mMetricsManager;
+ std::unique_ptr<JerkTracker> mJerkTracker;
+
+ std::unique_ptr<MotionPredictorMetricsManager> mMetricsManager;
const ReportAtomFunction mReportAtomFunction;
+
+ // Initialize prediction model and associated objects.
+ // Called during lazy initialization.
+ // TODO: b/210158587 Consider removing lazy initialization.
+ void initializeObjects();
};
} // namespace android
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
new file mode 100644
index 0000000..dcb25b7
--- /dev/null
+++ b/include/input/Resampler.h
@@ -0,0 +1,154 @@
+/**
+ * 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 <chrono>
+#include <optional>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/RingBuffer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Resampler is an interface for resampling MotionEvents. Every resampling implementation
+ * must use this interface to enable resampling inside InputConsumer's logic.
+ */
+struct Resampler {
+ virtual ~Resampler() = default;
+
+ /**
+ * Tries to resample motionEvent at frameTime. The provided frameTime must be greater than
+ * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
+ * frameTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
+ * may be resampled by another method, or not resampled at all. Furthermore, it is the
+ * implementer's responsibility to guarantee the following:
+ * - If resampling occurs, a single additional sample should be added to motionEvent. That is,
+ * if motionEvent had N samples before being passed to Resampler, then it will have N + 1
+ * samples by the end of the resampling. No other field of motionEvent should be modified.
+ * - If resampling does not occur, then motionEvent must not be modified in any way.
+ */
+ virtual void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) = 0;
+
+ /**
+ * Returns resample latency. Resample latency is the time difference between frame time and
+ * resample time. More precisely, let frameTime and resampleTime be two timestamps, and
+ * frameTime > resampleTime. Resample latency is defined as frameTime - resampleTime.
+ */
+ virtual std::chrono::nanoseconds getResampleLatency() const = 0;
+};
+
+class LegacyResampler final : public Resampler {
+public:
+ /**
+ * Tries to resample `motionEvent` at `frameTime` by adding a resampled sample at the end of
+ * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by
+ * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if
+ * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
+ * not null, interpolation will occur. If `futureSample` is null and there is enough historical
+ * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
+ * `motionEvent` is unmodified.
+ */
+ void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) override;
+
+ std::chrono::nanoseconds getResampleLatency() const override;
+
+private:
+ struct Pointer {
+ PointerProperties properties;
+ PointerCoords coords;
+ };
+
+ struct Sample {
+ std::chrono::nanoseconds eventTime;
+ std::vector<Pointer> pointers;
+
+ std::vector<PointerCoords> asPointerCoords() const {
+ std::vector<PointerCoords> pointersCoords;
+ for (const Pointer& pointer : pointers) {
+ pointersCoords.push_back(pointer.coords);
+ }
+ return pointersCoords;
+ }
+ };
+
+ /**
+ * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous
+ * and the current deviceId.
+ */
+ std::optional<DeviceId> mPreviousDeviceId;
+
+ /**
+ * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called.
+ * Note: We store up to two samples in order to simplify the implementation. Although,
+ * calculations are possible with only one previous sample.
+ */
+ RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
+
+ /**
+ * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
+ * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
+ * added to mLatestSamples.
+ */
+ void updateLatestSamples(const MotionEvent& motionEvent);
+
+ static Sample messageToSample(const InputMessage& message);
+
+ /**
+ * Checks if auxiliary sample has the same pointer properties of target sample. That is,
+ * auxiliary pointer IDs must appear in the same order as target pointer IDs, their toolType
+ * must match and be resampleable.
+ */
+ static bool pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary);
+
+ /**
+ * Checks if there are necessary conditions to interpolate. For example, interpolation cannot
+ * take place if samples are too far apart in time. mLatestSamples must have at least one sample
+ * when canInterpolate is invoked.
+ */
+ bool canInterpolate(const InputMessage& futureSample) const;
+
+ /**
+ * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
+ * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
+ * mLatestSamples must have at least one sample when attemptInterpolation is called.
+ */
+ std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
+ const InputMessage& futureSample) const;
+
+ /**
+ * Checks if there are necessary conditions to extrapolate. That is, there are at least two
+ * samples in mLatestSamples, and delta is bounded within a time interval.
+ */
+ bool canExtrapolate() const;
+
+ /**
+ * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from
+ * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime,
+ * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false,
+ * this function returns nullopt.
+ */
+ std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
+
+ inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
+};
+} // namespace android
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 728a8e1..49e909e 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -110,6 +110,9 @@
// High jerk means more predictions will be pruned, vice versa for low.
float lowJerk = 0;
float highJerk = 0;
+
+ // Coefficient for the first-order IIR filter for jerk calculation.
+ float jerkAlpha = 1;
};
// Creates a model from an encoded Flatbuffer model.
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index 222dac8..b6c6305 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -17,14 +17,30 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <input/Input.h>
+#include <map>
namespace android {
+enum class DeviceType {
+ KEYBOARD,
+ MOUSE,
+ TOUCHSCREEN,
+ DPAD,
+ STYLUS,
+ ROTARY_ENCODER,
+};
+
+android::base::unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth);
+
enum class UinputAction {
RELEASE = 0,
PRESS = 1,
MOVE = 2,
CANCEL = 3,
+ ftl_last = CANCEL,
};
class VirtualInputDevice {
@@ -77,6 +93,8 @@
private:
static const std::map<int, int> BUTTON_CODE_MAPPING;
+ int32_t mAccumulatedHighResScrollX;
+ int32_t mAccumulatedHighResScrollY;
};
class VirtualTouchscreen : public VirtualInputDevice {
@@ -122,4 +140,14 @@
bool handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime);
};
+class VirtualRotaryEncoder : public VirtualInputDevice {
+public:
+ VirtualRotaryEncoder(android::base::unique_fd fd);
+ virtual ~VirtualRotaryEncoder() override;
+ bool writeScrollEvent(float scrollAmount, std::chrono::nanoseconds eventTime);
+
+private:
+ int32_t mAccumulatedHighResScrollAmount;
+};
+
} // namespace android
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index 8c356d0..e5eee34 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -108,6 +108,10 @@
const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos, SessionTag tag);
+/**
+ * Forces FMQ to be enabled or disabled, for testing only.
+ */
+void APerformanceHint_setUseFMQForTesting(bool enabled);
__END_DECLS
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4c3f4a6..d1a5663 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -401,18 +401,10 @@
switch (driver) {
case GpuStatsInfo::Driver::GL:
case GpuStatsInfo::Driver::GL_UPDATED:
- case GpuStatsInfo::Driver::ANGLE: {
- if (mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::NONE ||
- mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::GL) {
- mGpuStats.glDriverToLoad = driver;
- break;
- }
-
- if (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE) {
- mGpuStats.glDriverFallback = driver;
- }
+ case GpuStatsInfo::Driver::ANGLE:
+ mGpuStats.glDriverToLoad = driver;
break;
- }
+
case GpuStatsInfo::Driver::VULKAN:
case GpuStatsInfo::Driver::VULKAN_UPDATED: {
if (mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::NONE ||
@@ -561,8 +553,7 @@
bool isIntendedDriverLoaded = false;
if (api == GpuStatsInfo::Api::API_GL) {
driver = mGpuStats.glDriverToLoad;
- isIntendedDriverLoaded =
- isDriverLoaded && (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE);
+ isIntendedDriverLoaded = isDriverLoaded;
} else {
driver = mGpuStats.vkDriverToLoad;
isIntendedDriverLoaded =
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 23f583b..72f29c6 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -125,6 +125,11 @@
VULKAN_DEVICE_EXTENSION = 9,
};
+ enum GLTelemetryHints {
+ NO_HINT = 0,
+ SKIP_TELEMETRY = 1,
+ };
+
GpuStatsInfo() = default;
GpuStatsInfo(const GpuStatsInfo&) = default;
virtual ~GpuStatsInfo() = default;
@@ -136,7 +141,6 @@
std::string appPackageName = "";
int32_t vulkanVersion = 0;
Driver glDriverToLoad = Driver::NONE;
- Driver glDriverFallback = Driver::NONE;
Driver vkDriverToLoad = Driver::NONE;
Driver vkDriverFallback = Driver::NONE;
bool glDriverToSend = false;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 51d2e53..1243b21 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -255,6 +255,7 @@
"BitTube.cpp",
"BLASTBufferQueue.cpp",
"BufferItemConsumer.cpp",
+ "BufferReleaseChannel.cpp",
"Choreographer.cpp",
"CompositorTiming.cpp",
"ConsumerBase.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 044170c..25e6a52 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -20,12 +20,16 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+#include <com_android_graphics_libgui_flags.h>
#include <cutils/atomic.h>
+#include <ftl/fake_guard.h>
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
#include <gui/FrameRateUtils.h>
#include <gui/GLConsumer.h>
@@ -39,7 +43,6 @@
#include <private/gui/ComposerServiceAIDL.h>
#include <android-base/thread_annotations.h>
-#include <chrono>
#include <com_android_graphics_libgui_flags.h>
@@ -74,6 +77,12 @@
std::unique_lock _lock{mutex}; \
base::ScopedLockAssertion assumeLocked(mutex);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+static ReleaseBufferCallback EMPTY_RELEASE_CALLBACK =
+ [](const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
+ std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {};
+#endif
+
void BLASTBufferItemConsumer::onDisconnect() {
Mutex::Autolock lock(mMutex);
mPreviouslyConnected = mCurrentlyConnected;
@@ -175,16 +184,21 @@
mSyncTransaction(nullptr),
mUpdateDestinationFrame(updateDestinationFrame) {
createBufferQueue(&mProducer, &mConsumer);
- // since the adapter is in the client process, set dequeue timeout
- // explicitly so that dequeueBuffer will block
- mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
-
- // safe default, most producers are expected to override this
- mProducer->setMaxDequeuedBufferCount(2);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
+#else
mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_HW_TEXTURE,
1, false, this);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // since the adapter is in the client process, set dequeue timeout
+ // explicitly so that dequeueBuffer will block
+ mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
+
static std::atomic<uint32_t> nextId = 0;
mProducerId = nextId++;
mName = name + "#" + std::to_string(mProducerId);
@@ -210,6 +224,12 @@
},
this);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> bufferReleaseConsumer;
+ gui::BufferReleaseChannel::open(mName, bufferReleaseConsumer, mBufferReleaseProducer);
+ mBufferReleaseReader = std::make_shared<BufferReleaseReader>(std::move(bufferReleaseConsumer));
+#endif
+
BQA_LOGV("BLASTBufferQueue created");
}
@@ -236,6 +256,14 @@
}
}
+void BLASTBufferQueue::onFirstRef() {
+ // safe default, most producers are expected to override this
+ mProducer->setMaxDequeuedBufferCount(2);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mBufferReleaseThread.start(sp<BLASTBufferQueue>::fromExisting(this));
+#endif
+}
+
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
int32_t format) {
LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
@@ -259,6 +287,9 @@
if (surfaceControlChanged) {
t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
layer_state_t::eEnableBackpressure);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer);
+#endif
applyTransaction = true;
}
mTransformHint = mSurfaceControl->getTransformHint();
@@ -296,14 +327,12 @@
return std::nullopt;
}
-static void transactionCommittedCallbackThunk(void* context, nsecs_t latchTime,
- const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats) {
- if (context == nullptr) {
- return;
- }
- sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
- bq->transactionCommittedCallback(latchTime, presentFence, stats);
+TransactionCompletedCallbackTakesContext BLASTBufferQueue::makeTransactionCommittedCallbackThunk() {
+ return [bbq = sp<BLASTBufferQueue>::fromExisting(
+ this)](void* /*context*/, nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
+ bbq->transactionCommittedCallback(latchTime, presentFence, stats);
+ };
}
void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
@@ -336,18 +365,15 @@
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
"empty.");
}
- decStrong((void*)transactionCommittedCallbackThunk);
}
}
-static void transactionCallbackThunk(void* context, nsecs_t latchTime,
- const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats) {
- if (context == nullptr) {
- return;
- }
- sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
- bq->transactionCallback(latchTime, presentFence, stats);
+TransactionCompletedCallbackTakesContext BLASTBufferQueue::makeTransactionCallbackThunk() {
+ return [bbq = sp<BLASTBufferQueue>::fromExisting(
+ this)](void* /*context*/, nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
+ bbq->transactionCallback(latchTime, presentFence, stats);
+ };
}
void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
@@ -381,6 +407,7 @@
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
auto currFrameNumber = stat.frameEventStats.frameNumber;
std::vector<ReleaseCallbackId> staleReleases;
for (const auto& [key, value]: mSubmitted) {
@@ -396,6 +423,7 @@
stat.currentMaxAcquiredBufferCount,
true /* fakeRelease */);
}
+#endif
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -403,23 +431,6 @@
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
"empty.");
}
-
- decStrong((void*)transactionCallbackThunk);
- }
-}
-
-// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
-// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
-// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
-// Otherwise, this is a no-op.
-static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
- const sp<Fence>& releaseFence,
- std::optional<uint32_t> currentMaxAcquiredBufferCount) {
- sp<BLASTBufferQueue> blastBufferQueue = context.promote();
- if (blastBufferQueue) {
- blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
- } else {
- ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
}
}
@@ -432,6 +443,23 @@
}
}
+// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
+// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
+// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
+// Otherwise, this is a no-op.
+ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() {
+ return [weakBbq = wp<BLASTBufferQueue>::fromExisting(
+ this)](const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount) {
+ sp<BLASTBufferQueue> bbq = weakBbq.promote();
+ if (!bbq) {
+ ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
+ return;
+ }
+ bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
+ };
+}
+
void BLASTBufferQueue::releaseBufferCallback(
const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount) {
@@ -594,9 +622,6 @@
t->notifyProducerDisconnect(mSurfaceControl);
}
- // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
- incStrong((void*)transactionCallbackThunk);
-
// Only update mSize for destination bounds if the incoming buffer matches the requested size.
// Otherwise, it could cause stretching since the destination bounds will update before the
// buffer with the new size is acquired.
@@ -609,9 +634,12 @@
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
bufferItem.mScalingMode, crop);
- auto releaseBufferCallback =
- std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
- std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ ReleaseBufferCallback releaseBufferCallback =
+ applyTransaction ? EMPTY_RELEASE_CALLBACK : makeReleaseBufferCallbackThunk();
+#else
+ auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
+#endif
sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
nsecs_t dequeueTime = -1;
@@ -629,7 +657,7 @@
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
- t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+ t->addTransactionCompletedCallback(makeTransactionCallbackThunk(), nullptr);
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
@@ -786,9 +814,9 @@
// Only need a commit callback when syncing to ensure the buffer that's synced has been
// sent to SF
- incStrong((void*)transactionCommittedCallbackThunk);
- mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
- static_cast<void*>(this));
+ mSyncTransaction
+ ->addTransactionCommittedCallback(makeTransactionCommittedCallbackThunk(),
+ nullptr);
if (mAcquireSingleBuffer) {
prevCallback = mTransactionReadyCallback;
prevTransaction = mSyncTransaction;
@@ -817,7 +845,7 @@
void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
std::lock_guard _lock{mTimestampMutex};
mDequeueTimestamps.erase(bufferId);
-};
+}
bool BLASTBufferQueue::syncNextTransaction(
std::function<void(SurfaceComposerClient::Transaction*)> callback,
@@ -1113,9 +1141,9 @@
// can be non-blocking when the producer is in the client process.
class BBQBufferQueueProducer : public BufferQueueProducer {
public:
- BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq)
+ BBQBufferQueueProducer(const sp<BufferQueueCore>& core, const wp<BLASTBufferQueue>& bbq)
: BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/),
- mBLASTBufferQueue(std::move(bbq)) {}
+ mBLASTBufferQueue(bbq) {}
status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
QueueBufferOutput* output) override {
@@ -1130,27 +1158,32 @@
// We want to resize the frame history when changing the size of the buffer queue
status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override {
int maxBufferCount;
- status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
- &maxBufferCount);
- // if we can't determine the max buffer count, then just skip growing the history size
- if (status == OK) {
- size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
- // optimize away resizing the frame history unless it will grow
- if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
- sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
- if (bbq != nullptr) {
- ALOGV("increasing frame history size to %zu", newFrameHistorySize);
- bbq->resizeFrameEventHistory(newFrameHistorySize);
- }
- }
+ if (status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
+ &maxBufferCount);
+ status != OK) {
+ return status;
}
- return status;
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ // if we can't determine the max buffer count, then just skip growing the history size
+ size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
+ // optimize away resizing the frame history unless it will grow
+ if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
+ ALOGV("increasing frame history size to %zu", newFrameHistorySize);
+ bbq->resizeFrameEventHistory(newFrameHistorySize);
+ }
+
+ return OK;
}
int query(int what, int* value) override {
if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
*value = 1;
- return NO_ERROR;
+ return OK;
}
return BufferQueueProducer::query(what, value);
}
@@ -1230,7 +1263,125 @@
void BLASTBufferQueue::setTransactionHangCallback(
std::function<void(const std::string&)> callback) {
std::lock_guard _lock{mMutex};
- mTransactionHangCallback = callback;
+ mTransactionHangCallback = std::move(callback);
}
+void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) {
+ std::lock_guard _lock{mMutex};
+ mApplyToken = std::move(applyToken);
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+
+BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(
+ std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> endpoint)
+ : mEndpoint{std::move(endpoint)} {
+ mEpollFd = android::base::unique_fd{epoll_create1(0)};
+ LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(),
+ "Failed to create buffer release epoll file descriptor. errno=%d "
+ "message='%s'",
+ errno, strerror(errno));
+
+ epoll_event registerEndpointFd{};
+ registerEndpointFd.events = EPOLLIN;
+ registerEndpointFd.data.fd = mEndpoint->getFd();
+ status_t status =
+ epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEndpoint->getFd(), ®isterEndpointFd);
+ LOG_ALWAYS_FATAL_IF(status == -1,
+ "Failed to register buffer release consumer file descriptor with epoll. "
+ "errno=%d message='%s'",
+ errno, strerror(errno));
+
+ mEventFd = android::base::unique_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ LOG_ALWAYS_FATAL_IF(!mEventFd.ok(),
+ "Failed to create buffer release event file descriptor. errno=%d "
+ "message='%s'",
+ errno, strerror(errno));
+
+ epoll_event registerEventFd{};
+ registerEventFd.events = EPOLLIN;
+ registerEventFd.data.fd = mEventFd.get();
+ status = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEventFd.get(), ®isterEventFd);
+ LOG_ALWAYS_FATAL_IF(status == -1,
+ "Failed to register buffer release event file descriptor with epoll. "
+ "errno=%d message='%s'",
+ errno, strerror(errno));
+}
+
+BLASTBufferQueue::BufferReleaseReader& BLASTBufferQueue::BufferReleaseReader::operator=(
+ BufferReleaseReader&& other) {
+ if (this != &other) {
+ ftl::FakeGuard guard{mMutex};
+ ftl::FakeGuard otherGuard{other.mMutex};
+ mEndpoint = std::move(other.mEndpoint);
+ mEpollFd = std::move(other.mEpollFd);
+ mEventFd = std::move(other.mEventFd);
+ }
+ return *this;
+}
+
+status_t BLASTBufferQueue::BufferReleaseReader::readBlocking(ReleaseCallbackId& outId,
+ sp<Fence>& outFence,
+ uint32_t& outMaxAcquiredBufferCount) {
+ epoll_event event{};
+ while (true) {
+ int eventCount = epoll_wait(mEpollFd.get(), &event, 1 /* maxevents */, -1 /* timeout */);
+ if (eventCount == 1) {
+ break;
+ }
+ if (eventCount == -1 && errno != EINTR) {
+ ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno,
+ strerror(errno));
+ }
+ }
+
+ if (event.data.fd == mEventFd.get()) {
+ uint64_t value;
+ if (read(mEventFd.get(), &value, sizeof(uint64_t)) == -1 && errno != EWOULDBLOCK) {
+ ALOGE("error while reading from eventfd. errno=%d message='%s'", errno,
+ strerror(errno));
+ }
+ return WOULD_BLOCK;
+ }
+
+ std::lock_guard lock{mMutex};
+ return mEndpoint->readReleaseFence(outId, outFence, outMaxAcquiredBufferCount);
+}
+
+void BLASTBufferQueue::BufferReleaseReader::interruptBlockingRead() {
+ uint64_t value = 1;
+ if (write(mEventFd.get(), &value, sizeof(uint64_t)) == -1) {
+ ALOGE("failed to notify dequeue event. errno=%d message='%s'", errno, strerror(errno));
+ }
+}
+
+void BLASTBufferQueue::BufferReleaseThread::start(const sp<BLASTBufferQueue>& bbq) {
+ mRunning = std::make_shared<std::atomic_bool>(true);
+ mReader = bbq->mBufferReleaseReader;
+ std::thread([running = mRunning, reader = mReader, weakBbq = wp<BLASTBufferQueue>(bbq)]() {
+ pthread_setname_np(pthread_self(), "BufferReleaseThread");
+ while (*running) {
+ ReleaseCallbackId id;
+ sp<Fence> fence;
+ uint32_t maxAcquiredBufferCount;
+ if (status_t status = reader->readBlocking(id, fence, maxAcquiredBufferCount);
+ status != OK) {
+ continue;
+ }
+ sp<BLASTBufferQueue> bbq = weakBbq.promote();
+ if (!bbq) {
+ return;
+ }
+ bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+ }
+ }).detach();
+}
+
+BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() {
+ *mRunning = false;
+ mReader->interruptBlockingRead();
+}
+
+#endif
+
} // namespace android
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index e6331e7..bfe3d6e 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -21,8 +21,11 @@
#include <inttypes.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
+#include <ui/BufferQueueDefs.h>
+#include <ui/GraphicBuffer.h>
#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
// #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -32,18 +35,37 @@
namespace android {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp, bool isConsumerSurfaceFlinger)
+ : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger) {
+ initialize(consumerUsage, bufferCount);
+}
+
+BufferItemConsumer::BufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp)
+ : ConsumerBase(producer, consumer, controlledByApp) {
+ initialize(consumerUsage, bufferCount);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
BufferItemConsumer::BufferItemConsumer(
const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount, bool controlledByApp) :
ConsumerBase(consumer, controlledByApp)
{
+ initialize(consumerUsage, bufferCount);
+}
+
+void BufferItemConsumer::initialize(uint64_t consumerUsage, int bufferCount) {
status_t err = mConsumer->setConsumerUsageBits(consumerUsage);
- LOG_ALWAYS_FATAL_IF(err != OK,
- "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
if (bufferCount != DEFAULT_MAX_BUFFERS) {
err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
- LOG_ALWAYS_FATAL_IF(err != OK,
- "Failed to set max acquired buffer count to %d", bufferCount);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set max acquired buffer count to %d",
+ bufferCount);
}
}
@@ -87,17 +109,38 @@
status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence) {
- status_t err;
+ Mutex::Autolock _l(mMutex);
+ return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
+}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence) {
Mutex::Autolock _l(mMutex);
- err = addReleaseFenceLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int slotIndex = getSlotForBufferLocked(buffer);
+ if (slotIndex == INVALID_BUFFER_SLOT) {
+ return BAD_VALUE;
+ }
+
+ return releaseBufferSlotLocked(slotIndex, buffer, releaseFence);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence) {
+ status_t err;
+
+ err = addReleaseFenceLocked(slotIndex, buffer, releaseFence);
if (err != OK) {
BI_LOGE("Failed to addReleaseFenceLocked");
}
- err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ err = releaseBufferLocked(slotIndex, buffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
if (err != OK && err != IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
BI_LOGE("Failed to release buffer: %s (%d)",
strerror(-err), err);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index ead9f0f..831b2ee 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -45,7 +45,10 @@
#include <system/window.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
+using namespace com::android::graphics::libgui;
// Macros for include BufferQueueCore information in log messages
#define BQ_LOGV(x, ...) \
@@ -924,6 +927,7 @@
uint64_t currentFrameNumber = 0;
BufferItem item;
int connectedApi;
+ bool enableEglCpuThrottling = true;
sp<Fence> lastQueuedFence;
{ // Autolock scope
@@ -1097,6 +1101,9 @@
VALIDATE_CONSISTENCY();
connectedApi = mCore->mConnectedApi;
+ if (flags::bq_producer_throttles_only_async_mode()) {
+ enableEglCpuThrottling = mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock;
+ }
lastQueuedFence = std::move(mLastQueueBufferFence);
mLastQueueBufferFence = std::move(acquireFence);
@@ -1142,7 +1149,7 @@
}
// Wait without lock held
- if (connectedApi == NATIVE_WINDOW_API_EGL) {
+ if (connectedApi == NATIVE_WINDOW_API_EGL && enableEglCpuThrottling) {
// Waiting here allows for two full buffers to be queued but not a
// third. In the event that frames take varying time, this makes a
// small trade-off in favor of latency rather than throughput.
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
new file mode 100644
index 0000000..27367aa
--- /dev/null
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -0,0 +1,358 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BufferReleaseChannel"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <android-base/result.h>
+#include <android/binder_status.h>
+#include <binder/Parcel.h>
+#include <utils/Flattenable.h>
+
+#include <gui/BufferReleaseChannel.h>
+#include <private/gui/ParcelUtils.h>
+
+using android::base::Result;
+
+namespace android::gui {
+
+namespace {
+
+template <typename T>
+static void readAligned(const void*& buffer, size_t& size, T& value) {
+ size -= FlattenableUtils::align<alignof(T)>(buffer);
+ FlattenableUtils::read(buffer, size, value);
+}
+
+template <typename T>
+static void writeAligned(void*& buffer, size_t& size, T value) {
+ size -= FlattenableUtils::align<alignof(T)>(buffer);
+ FlattenableUtils::write(buffer, size, value);
+}
+
+template <typename T>
+static void addAligned(size_t& size, T /* value */) {
+ size = FlattenableUtils::align<sizeof(T)>(size);
+ size += sizeof(T);
+}
+
+template <typename T>
+static inline constexpr uint32_t low32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n));
+}
+
+template <typename T>
+static inline constexpr uint32_t high32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
+}
+
+template <typename T>
+static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+ return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
+}
+
+} // namespace
+
+size_t BufferReleaseChannel::Message::getPodSize() const {
+ size_t size = 0;
+ addAligned(size, low32(releaseCallbackId.bufferId));
+ addAligned(size, high32(releaseCallbackId.bufferId));
+ addAligned(size, low32(releaseCallbackId.framenumber));
+ addAligned(size, high32(releaseCallbackId.framenumber));
+ addAligned(size, maxAcquiredBufferCount);
+ return size;
+}
+
+size_t BufferReleaseChannel::Message::getFlattenedSize() const {
+ size_t size = releaseFence->getFlattenedSize();
+ size = FlattenableUtils::align<4>(size);
+ size += getPodSize();
+ return size;
+}
+
+status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) {
+ return err;
+ }
+ size -= FlattenableUtils::align<4>(buffer);
+
+ // Check we still have enough space
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ writeAligned(buffer, size, low32(releaseCallbackId.bufferId));
+ writeAligned(buffer, size, high32(releaseCallbackId.bufferId));
+ writeAligned(buffer, size, low32(releaseCallbackId.framenumber));
+ writeAligned(buffer, size, high32(releaseCallbackId.framenumber));
+ writeAligned(buffer, size, maxAcquiredBufferCount);
+ return OK;
+}
+
+status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ releaseFence = new Fence();
+ if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
+ return err;
+ }
+ size -= FlattenableUtils::align<4>(buffer);
+
+ // Check we still have enough space
+ if (size < getPodSize()) {
+ return OK;
+ }
+
+ uint32_t bufferIdLo = 0, bufferIdHi = 0;
+ uint32_t frameNumberLo = 0, frameNumberHi = 0;
+
+ readAligned(buffer, size, bufferIdLo);
+ readAligned(buffer, size, bufferIdHi);
+ releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi);
+ readAligned(buffer, size, frameNumberLo);
+ readAligned(buffer, size, frameNumberHi);
+ releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
+ readAligned(buffer, size, maxAcquiredBufferCount);
+
+ return OK;
+}
+
+status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
+ ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
+ uint32_t& outMaxAcquiredBufferCount) {
+ Message message;
+ mFlattenedBuffer.resize(message.getFlattenedSize());
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+
+ iovec iov{
+ .iov_base = mFlattenedBuffer.data(),
+ .iov_len = mFlattenedBuffer.size(),
+ };
+
+ msghdr msg{
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = controlMessageBuffer.data(),
+ .msg_controllen = controlMessageBuffer.size(),
+ };
+
+ int result;
+ do {
+ result = recvmsg(mFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ return WOULD_BLOCK;
+ }
+ ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ if (msg.msg_iovlen != 1) {
+ ALOGE("Error reading release fence from socket: bad data length");
+ return UNKNOWN_ERROR;
+ }
+
+ if (msg.msg_controllen % sizeof(int) != 0) {
+ ALOGE("Error reading release fence from socket: bad fd length");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t dataLen = msg.msg_iov->iov_len;
+ const void* data = static_cast<const void*>(msg.msg_iov->iov_base);
+ if (!data) {
+ ALOGE("Error reading release fence from socket: no buffer data");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t fdCount = 0;
+ const int* fdData = nullptr;
+ if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) {
+ fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+ fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ }
+
+ if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) {
+ return err;
+ }
+
+ outReleaseCallbackId = message.releaseCallbackId;
+ outReleaseFence = std::move(message.releaseFence);
+ outMaxAcquiredBufferCount = message.maxAcquiredBufferCount;
+
+ return OK;
+}
+
+int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
+ const sp<Fence>& fence,
+ uint32_t maxAcquiredBufferCount) {
+ Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
+ mFlattenedBuffer.resize(message.getFlattenedSize());
+ int flattenedFd;
+ {
+ // Make copies of needed items since flatten modifies them, and we don't
+ // want to send anything if there's an error during flatten.
+ void* flattenedBufferPtr = mFlattenedBuffer.data();
+ size_t flattenedBufferSize = mFlattenedBuffer.size();
+ int* flattenedFdPtr = &flattenedFd;
+ size_t flattenedFdCount = 1;
+ if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
+ flattenedFdCount);
+ err != OK) {
+ ALOGE("Failed to flatten BufferReleaseChannel message.");
+ return err;
+ }
+ }
+
+ iovec iov{
+ .iov_base = mFlattenedBuffer.data(),
+ .iov_len = mFlattenedBuffer.size(),
+ };
+
+ msghdr msg{
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ if (fence && fence->isValid()) {
+ msg.msg_control = controlMessageBuffer.data();
+ msg.msg_controllen = controlMessageBuffer.size();
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
+ }
+
+ int result;
+ do {
+ result = sendmsg(mFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) {
+ if (!parcel) return STATUS_BAD_VALUE;
+ SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName);
+ SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd);
+ return STATUS_OK;
+}
+
+status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const {
+ if (!parcel) return STATUS_BAD_VALUE;
+ SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName);
+ SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd);
+ return STATUS_OK;
+}
+
+status_t BufferReleaseChannel::open(std::string name,
+ std::unique_ptr<ConsumerEndpoint>& outConsumer,
+ std::shared_ptr<ProducerEndpoint>& outProducer) {
+ outConsumer.reset();
+ outProducer.reset();
+
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
+ ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ android::base::unique_fd consumerFd(sockets[0]);
+ android::base::unique_fd producerFd(sockets[1]);
+
+ // Socket buffer size. The default is typically about 128KB, which is much larger than
+ // we really need.
+ size_t bufferSize = 32 * 1024;
+ if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Configure the consumer socket to be non-blocking.
+ int flags = fcntl(consumerFd.get(), F_GETFL, 0);
+ if (flags == -1) {
+ ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(),
+ errno, strerror(errno));
+ return -errno;
+ }
+ if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
+ ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Configure a timeout for the producer socket.
+ const timeval timeout{.tv_sec = 1, .tv_usec = 0};
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) {
+ ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(),
+ errno, strerror(errno));
+ return -errno;
+ }
+
+ // Make the consumer read-only
+ if (shutdown(consumerFd.get(), SHUT_WR) == -1) {
+ ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Make the producer write-only
+ if (shutdown(producerFd.get(), SHUT_RD) == -1) {
+ ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
+ outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
+ return STATUS_OK;
+}
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index b625c3f..602bba8 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <inttypes.h>
-
#define LOG_TAG "ConsumerBase"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -29,17 +27,23 @@
#include <cutils/atomic.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
+#include <log/log.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
+#include <inttypes.h>
+
// Macros for including the ConsumerBase name in log messages
#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
// #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -59,6 +63,30 @@
mAbandoned(false),
mConsumer(bufferQueue),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ initialize(controlledByApp);
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger)
+ : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
+ mSurface = sp<Surface>::make(producer, controlledByApp);
+ initialize(controlledByApp);
+}
+
+ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp)
+ : mAbandoned(false),
+ mConsumer(consumer),
+ mSurface(sp<Surface>::make(producer, controlledByApp)),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ initialize(controlledByApp);
+}
+
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+void ConsumerBase::initialize(bool controlledByApp) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -96,6 +124,35 @@
abandon();
}
+int ConsumerBase::getSlotForBufferLocked(const sp<GraphicBuffer>& buffer) {
+ if (!buffer) {
+ return BufferQueue::INVALID_BUFFER_SLOT;
+ }
+
+ uint64_t id = buffer->getId();
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+ auto& slot = mSlots[i];
+ if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) {
+ return i;
+ }
+ }
+
+ return BufferQueue::INVALID_BUFFER_SLOT;
+}
+
+status_t ConsumerBase::detachBufferLocked(int slotIndex) {
+ status_t result = mConsumer->detachBuffer(slotIndex);
+
+ if (result != NO_ERROR) {
+ CB_LOGE("Failed to detach buffer: %d", result);
+ return result;
+ }
+
+ freeBufferLocked(slotIndex);
+
+ return result;
+}
+
void ConsumerBase::freeBufferLocked(int slotIndex) {
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mSlots[slotIndex].mGraphicBuffer = nullptr;
@@ -252,16 +309,30 @@
return NO_INIT;
}
- status_t result = mConsumer->detachBuffer(slot);
- if (result != NO_ERROR) {
- CB_LOGE("Failed to detach buffer: %d", result);
- return result;
+ return detachBufferLocked(slot);
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t ConsumerBase::detachBuffer(const sp<GraphicBuffer>& buffer) {
+ CB_LOGV("detachBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ CB_LOGE("detachBuffer: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ if (buffer == nullptr) {
+ return BAD_VALUE;
}
- freeBufferLocked(slot);
+ int slotIndex = getSlotForBufferLocked(buffer);
+ if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) {
+ return BAD_VALUE;
+ }
- return result;
+ return detachBufferLocked(slotIndex);
}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) {
Mutex::Autolock _l(mMutex);
@@ -309,6 +380,17 @@
return mConsumer->setTransformHint(hint);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+status_t ConsumerBase::setMaxBufferCount(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->setMaxBufferCount(bufferCount);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -318,6 +400,17 @@
return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("setConsumerIsProtected: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->setConsumerIsProtected(isProtected);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
sp<NativeHandle> ConsumerBase::getSidebandStream() const {
Mutex::Autolock _l(mMutex);
if (mAbandoned) {
@@ -384,6 +477,19 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+sp<Surface> ConsumerBase::getSurface() const {
+ LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
+ "It's illegal to get the surface of a Consumer that does not own it. This "
+ "should be impossible once the old CTOR is removed.");
+ return mSurface;
+}
+
+sp<IGraphicBufferConsumer> ConsumerBase::getIGraphicBufferConsumer() const {
+ return mConsumer;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
nsecs_t presentWhen, uint64_t maxFrameNumber) {
if (mAbandoned) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 3031fa1..23b432e 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -18,9 +18,9 @@
#define LOG_TAG "CpuConsumer"
//#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <gui/CpuConsumer.h>
-
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
+#include <gui/CpuConsumer.h>
#include <utils/Log.h>
#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -31,12 +31,25 @@
namespace android {
-CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
- size_t maxLockedBuffers, bool controlledByApp) :
- ConsumerBase(bq, controlledByApp),
- mMaxLockedBuffers(maxLockedBuffers),
- mCurrentLockedBuffers(0)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp,
+ bool isConsumerSurfaceFlinger)
+ : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger),
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0) {
+ // Create tracking entries for locked buffers
+ mAcquiredBuffers.insertAt(0, maxLockedBuffers);
+
+ mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN);
+ mConsumer->setMaxAcquiredBufferCount(static_cast<int32_t>(maxLockedBuffers));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp)
+ : ConsumerBase(bq, controlledByApp),
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0) {
// Create tracking entries for locked buffers
mAcquiredBuffers.insertAt(0, maxLockedBuffers);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index d49489c..95cce5c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -101,6 +101,34 @@
return hasIt;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(true) {
+ GLC_LOGV("GLConsumer");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp),
@@ -130,27 +158,54 @@
mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
-GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
- bool useFenceSync, bool isControlledByApp) :
- ConsumerBase(bq, isControlledByApp),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
- mCurrentFrameNumber(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mTexName(0),
- mUseFenceSync(useFenceSync),
- mTexTarget(texTarget),
- mEglDisplay(EGL_NO_DISPLAY),
- mEglContext(EGL_NO_CONTEXT),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
- mAttached(false)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+GLConsumer::GLConsumer(uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(false) {
+ GLC_LOGV("GLConsumer");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, bool useFenceSync,
+ bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(false) {
GLC_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ff6b558..2699368 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -62,7 +62,7 @@
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index f5d19aa..83fc827 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -43,12 +43,6 @@
} // Anonymous namespace
-namespace { // Anonymous
-
-constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2;
-
-} // Anonymous namespace
-
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t err = output->writeUint64(frameNumber);
if (err != NO_ERROR) return err;
@@ -119,23 +113,6 @@
return err;
}
-JankData::JankData()
- : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {}
-
-status_t JankData::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(output->writeInt64, frameVsyncId);
- SAFE_PARCEL(output->writeInt32, jankType);
- SAFE_PARCEL(output->writeInt64, frameIntervalNs);
- return NO_ERROR;
-}
-
-status_t JankData::readFromParcel(const Parcel* input) {
- SAFE_PARCEL(input->readInt64, &frameVsyncId);
- SAFE_PARCEL(input->readInt32, &jankType);
- SAFE_PARCEL(input->readInt64, &frameIntervalNs);
- return NO_ERROR;
-}
-
status_t SurfaceStats::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) {
@@ -160,10 +137,6 @@
SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount);
SAFE_PARCEL(output->writeParcelable, eventStats);
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size()));
- for (const auto& data : jankData) {
- SAFE_PARCEL(output->writeParcelable, data);
- }
SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId);
return NO_ERROR;
}
@@ -200,13 +173,6 @@
SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount);
SAFE_PARCEL(input->readParcelable, &eventStats);
- int32_t jankData_size = 0;
- SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize());
- for (int i = 0; i < jankData_size; i++) {
- JankData data;
- SAFE_PARCEL(input->readParcelable, &data);
- jankData.push_back(data);
- }
SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId);
return NO_ERROR;
}
@@ -371,11 +337,7 @@
status_t CallbackId::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeInt64, id);
- if (type == Type::ON_COMPLETE && includeJankData) {
- SAFE_PARCEL(output->writeInt32, kSerializedCallbackTypeOnCompelteWithJankData);
- } else {
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
- }
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
return NO_ERROR;
}
@@ -383,13 +345,7 @@
SAFE_PARCEL(input->readInt64, &id);
int32_t typeAsInt;
SAFE_PARCEL(input->readInt32, &typeAsInt);
- if (typeAsInt == kSerializedCallbackTypeOnCompelteWithJankData) {
- type = Type::ON_COMPLETE;
- includeJankData = true;
- } else {
- type = static_cast<CallbackId::Type>(typeAsInt);
- includeJankData = false;
- }
+ type = static_cast<CallbackId::Type>(typeAsInt);
return NO_ERROR;
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 3745805..b109969 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "LayerState"
#include <cinttypes>
-#include <cmath>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
@@ -177,6 +176,7 @@
}
SAFE_PARCEL(output.write, stretchEffect);
+ SAFE_PARCEL(output.writeParcelable, edgeExtensionParameters);
SAFE_PARCEL(output.write, bufferCrop);
SAFE_PARCEL(output.write, destinationFrame);
SAFE_PARCEL(output.writeInt32, static_cast<uint32_t>(trustedOverlay));
@@ -193,6 +193,13 @@
SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio);
SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio);
SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint));
+
+ const bool hasBufferReleaseChannel = (bufferReleaseChannel != nullptr);
+ SAFE_PARCEL(output.writeBool, hasBufferReleaseChannel);
+ if (hasBufferReleaseChannel) {
+ SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
+ }
+
return NO_ERROR;
}
@@ -306,6 +313,7 @@
}
SAFE_PARCEL(input.read, stretchEffect);
+ SAFE_PARCEL(input.readParcelable, &edgeExtensionParameters);
SAFE_PARCEL(input.read, bufferCrop);
SAFE_PARCEL(input.read, destinationFrame);
uint32_t trustedOverlayInt;
@@ -337,6 +345,13 @@
SAFE_PARCEL(input.readInt32, &tmpInt32);
cachingHint = static_cast<gui::CachingHint>(tmpInt32);
+ bool hasBufferReleaseChannel;
+ SAFE_PARCEL(input.readBool, &hasBufferReleaseChannel);
+ if (hasBufferReleaseChannel) {
+ bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
+ SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
+ }
+
return NO_ERROR;
}
@@ -682,6 +697,10 @@
what |= eStretchChanged;
stretchEffect = other.stretchEffect;
}
+ if (other.what & eEdgeExtensionChanged) {
+ what |= eEdgeExtensionChanged;
+ edgeExtensionParameters = other.edgeExtensionParameters;
+ }
if (other.what & eBufferCropChanged) {
what |= eBufferCropChanged;
bufferCrop = other.bufferCrop;
@@ -712,6 +731,10 @@
if (other.what & eFlushJankData) {
what |= eFlushJankData;
}
+ if (other.what & eBufferReleaseChannelChanged) {
+ what |= eBufferReleaseChannelChanged;
+ bufferReleaseChannel = other.bufferReleaseChannel;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -783,6 +806,7 @@
CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
CHECK_DIFF(diff, eTrustedOverlayChanged, other, trustedOverlay);
CHECK_DIFF(diff, eStretchChanged, other, stretchEffect);
+ CHECK_DIFF(diff, eEdgeExtensionChanged, other, edgeExtensionParameters);
CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop);
CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame);
if (other.what & eProducerDisconnect) diff |= eProducerDisconnect;
@@ -790,6 +814,7 @@
CHECK_DIFF(diff, eColorChanged, other, color.rgb);
CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
+ if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
return diff;
}
@@ -867,88 +892,6 @@
// ----------------------------------------------------------------------------
-namespace gui {
-
-status_t CaptureArgs::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(pixelFormat));
- SAFE_PARCEL(output->write, sourceCrop);
- SAFE_PARCEL(output->writeFloat, frameScaleX);
- SAFE_PARCEL(output->writeFloat, frameScaleY);
- SAFE_PARCEL(output->writeBool, captureSecureLayers);
- SAFE_PARCEL(output->writeInt32, uid);
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace));
- SAFE_PARCEL(output->writeBool, allowProtected);
- SAFE_PARCEL(output->writeBool, grayscale);
- SAFE_PARCEL(output->writeInt32, excludeHandles.size());
- for (auto& excludeHandle : excludeHandles) {
- SAFE_PARCEL(output->writeStrongBinder, excludeHandle);
- }
- SAFE_PARCEL(output->writeBool, hintForSeamlessTransition);
- return NO_ERROR;
-}
-
-status_t CaptureArgs::readFromParcel(const Parcel* input) {
- int32_t value = 0;
- SAFE_PARCEL(input->readInt32, &value);
- pixelFormat = static_cast<ui::PixelFormat>(value);
- SAFE_PARCEL(input->read, sourceCrop);
- SAFE_PARCEL(input->readFloat, &frameScaleX);
- SAFE_PARCEL(input->readFloat, &frameScaleY);
- SAFE_PARCEL(input->readBool, &captureSecureLayers);
- SAFE_PARCEL(input->readInt32, &uid);
- SAFE_PARCEL(input->readInt32, &value);
- dataspace = static_cast<ui::Dataspace>(value);
- SAFE_PARCEL(input->readBool, &allowProtected);
- SAFE_PARCEL(input->readBool, &grayscale);
- int32_t numExcludeHandles = 0;
- SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize());
- excludeHandles.reserve(numExcludeHandles);
- for (int i = 0; i < numExcludeHandles; i++) {
- sp<IBinder> binder;
- SAFE_PARCEL(input->readStrongBinder, &binder);
- excludeHandles.emplace(binder);
- }
- SAFE_PARCEL(input->readBool, &hintForSeamlessTransition);
- return NO_ERROR;
-}
-
-status_t DisplayCaptureArgs::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(CaptureArgs::writeToParcel, output);
-
- SAFE_PARCEL(output->writeStrongBinder, displayToken);
- SAFE_PARCEL(output->writeUint32, width);
- SAFE_PARCEL(output->writeUint32, height);
- return NO_ERROR;
-}
-
-status_t DisplayCaptureArgs::readFromParcel(const Parcel* input) {
- SAFE_PARCEL(CaptureArgs::readFromParcel, input);
-
- SAFE_PARCEL(input->readStrongBinder, &displayToken);
- SAFE_PARCEL(input->readUint32, &width);
- SAFE_PARCEL(input->readUint32, &height);
- return NO_ERROR;
-}
-
-status_t LayerCaptureArgs::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(CaptureArgs::writeToParcel, output);
-
- SAFE_PARCEL(output->writeStrongBinder, layerHandle);
- SAFE_PARCEL(output->writeBool, childrenOnly);
- return NO_ERROR;
-}
-
-status_t LayerCaptureArgs::readFromParcel(const Parcel* input) {
- SAFE_PARCEL(CaptureArgs::readFromParcel, input);
-
- SAFE_PARCEL(input->readStrongBinder, &layerHandle);
-
- SAFE_PARCEL(input->readBool, &childrenOnly);
- return NO_ERROR;
-}
-
-}; // namespace gui
-
ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
uint64_t bufferId;
if (buffer) {
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index 601a5f9..2de023e 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -40,6 +40,13 @@
SAFE_PARCEL(parcel->writeBool, capturedSecureLayers);
SAFE_PARCEL(parcel->writeBool, capturedHdrLayers);
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace));
+ if (optionalGainMap != nullptr) {
+ SAFE_PARCEL(parcel->writeBool, true);
+ SAFE_PARCEL(parcel->write, *optionalGainMap);
+ } else {
+ SAFE_PARCEL(parcel->writeBool, false);
+ }
+ SAFE_PARCEL(parcel->writeFloat, hdrSdrRatio);
return NO_ERROR;
}
@@ -68,6 +75,14 @@
uint32_t dataspace = 0;
SAFE_PARCEL(parcel->readUint32, &dataspace);
capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+
+ bool hasGainmap;
+ SAFE_PARCEL(parcel->readBool, &hasGainmap);
+ if (hasGainmap) {
+ optionalGainMap = new GraphicBuffer();
+ SAFE_PARCEL(parcel->read, *optionalGainMap);
+ }
+ SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio);
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 87fd448..66e7ddd 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -21,6 +21,8 @@
#include <gui/Surface.h>
#include <condition_variable>
+#include <cstddef>
+#include <cstdint>
#include <deque>
#include <mutex>
#include <thread>
@@ -41,11 +43,9 @@
#include <ui/GraphicBuffer.h>
#include <ui/Region.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItem.h>
-#include <gui/IProducerListener.h>
-
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <private/gui/ComposerService.h>
@@ -77,9 +77,28 @@
} // namespace
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+Surface::ProducerDeathListenerProxy::ProducerDeathListenerProxy(wp<SurfaceListener> surfaceListener)
+ : mSurfaceListener(surfaceListener) {}
+
+void Surface::ProducerDeathListenerProxy::binderDied(const wp<IBinder>&) {
+ sp<SurfaceListener> surfaceListener = mSurfaceListener.promote();
+ if (!surfaceListener) {
+ return;
+ }
+
+ if (surfaceListener->needsDeathNotify()) {
+ surfaceListener->onRemoteDied();
+ }
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,
const sp<IBinder>& surfaceControlHandle)
: mGraphicBufferProducer(bufferProducer),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ mSurfaceDeathListener(nullptr),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
mGenerationNumber(0),
@@ -134,6 +153,12 @@
if (mConnectedToCpu) {
Surface::disconnect(NATIVE_WINDOW_API_CPU);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ if (mSurfaceDeathListener != nullptr) {
+ IInterface::asBinder(mGraphicBufferProducer)->unlinkToDeath(mSurfaceDeathListener);
+ mSurfaceDeathListener = nullptr;
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
}
sp<ISurfaceComposer> Surface::composerService() const {
@@ -163,6 +188,12 @@
mReqFormat, mReqUsage);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t Surface::allowAllocation(bool allowAllocation) {
+ return mGraphicBufferProducer->allowAllocation(allowAllocation);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
status_t Surface::setGenerationNumber(uint32_t generation) {
status_t result = mGraphicBufferProducer->setGenerationNumber(generation);
if (result == NO_ERROR) {
@@ -695,6 +726,51 @@
return OK;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence) {
+ if (buffer == nullptr || outFence == nullptr) {
+ return BAD_VALUE;
+ }
+
+ android_native_buffer_t* anb;
+ int fd = -1;
+ status_t res = dequeueBuffer(&anb, &fd);
+ *buffer = GraphicBuffer::from(anb);
+ *outFence = sp<Fence>::make(fd);
+ return res;
+}
+
+status_t Surface::queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd,
+ SurfaceQueueBufferOutput* output) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+ return queueBuffer(buffer.get(), fd ? fd->get() : -1, output);
+}
+
+status_t Surface::detachBuffer(const sp<GraphicBuffer>& buffer) {
+ if (nullptr == buffer) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ uint64_t bufferId = buffer->getId();
+ for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) {
+ auto& bufferSlot = mSlots[slot];
+ if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) {
+ bufferSlot.buffer = nullptr;
+ bufferSlot.dirtyRegion = Region::INVALID_REGION;
+ return mGraphicBufferProducer->detachBuffer(slot);
+ }
+ }
+
+ return BAD_VALUE;
+}
+
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) {
using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
@@ -1118,6 +1194,140 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd,
+ SurfaceQueueBufferOutput* surfaceOutput) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffer");
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferInput input;
+ int slot;
+ sp<Fence> fence;
+ {
+ Mutex::Autolock lock(mMutex);
+
+ slot = getSlotFromBufferLocked(buffer);
+ if (slot < 0) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return slot;
+ }
+ if (mSharedBufferSlot == slot && mSharedBufferHasBeenQueued) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);
+ applyGrallocMetadataLocked(buffer, input);
+ fence = input.fence;
+ }
+ nsecs_t now = systemTime();
+ // Drop the lock temporarily while we touch the underlying producer. In the case of a local
+ // BufferQueue, the following should be allowable:
+ //
+ // Surface::queueBuffer
+ // -> IConsumerListener::onFrameAvailable callback triggers automatically
+ // -> implementation calls IGraphicBufferConsumer::acquire/release immediately
+ // -> SurfaceListener::onBufferRelesed callback triggers automatically
+ // -> implementation calls Surface::dequeueBuffer
+ status_t err = mGraphicBufferProducer->queueBuffer(slot, input, &output);
+ {
+ Mutex::Autolock lock(mMutex);
+
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("queueBuffer: error queuing buffer, %d", err);
+ }
+
+ onBufferQueuedLocked(slot, fence, output);
+ }
+
+ if (surfaceOutput != nullptr) {
+ *surfaceOutput = {.bufferReplaced = output.bufferReplaced};
+ }
+
+ return err;
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers,
+ std::vector<SurfaceQueueBufferOutput>* queueBufferOutputs)
+#else
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers)
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+{
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffers");
+
+ size_t numBuffers = buffers.size();
+ std::vector<IGraphicBufferProducer::QueueBufferInput> igbpQueueBufferInputs(numBuffers);
+ std::vector<IGraphicBufferProducer::QueueBufferOutput> igbpQueueBufferOutputs;
+ std::vector<int> bufferSlots(numBuffers, -1);
+ std::vector<sp<Fence>> bufferFences(numBuffers);
+
+ int err;
+ {
+ Mutex::Autolock lock(mMutex);
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ int i = getSlotFromBufferLocked(buffers[batchIdx].buffer);
+ if (i < 0) {
+ if (buffers[batchIdx].fenceFd >= 0) {
+ close(buffers[batchIdx].fenceFd);
+ }
+ return i;
+ }
+ bufferSlots[batchIdx] = i;
+
+ IGraphicBufferProducer::QueueBufferInput input;
+ getQueueBufferInputLocked(buffers[batchIdx].buffer, buffers[batchIdx].fenceFd,
+ buffers[batchIdx].timestamp, &input);
+ input.slot = i;
+ bufferFences[batchIdx] = input.fence;
+ igbpQueueBufferInputs[batchIdx] = input;
+ }
+ }
+ nsecs_t now = systemTime();
+ err = mGraphicBufferProducer->queueBuffers(igbpQueueBufferInputs, &igbpQueueBufferOutputs);
+ {
+ Mutex::Autolock lock(mMutex);
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err);
+ }
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx],
+ igbpQueueBufferOutputs[batchIdx]);
+ }
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ if (queueBufferOutputs != nullptr) {
+ queueBufferOutputs->clear();
+ queueBufferOutputs->resize(numBuffers);
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ (*queueBufferOutputs)[batchIdx].bufferReplaced =
+ igbpQueueBufferOutputs[batchIdx].bufferReplaced;
+ }
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+ return err;
+}
+
+#else
+
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
@@ -1205,6 +1415,8 @@
return err;
}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
void Surface::querySupportedTimestampsLocked() const {
// mMutex must be locked when calling this method.
@@ -1860,30 +2072,23 @@
}
int Surface::connect(int api) {
- static sp<IProducerListener> listener = new StubProducerListener();
+ static sp<SurfaceListener> listener = new StubSurfaceListener();
return connect(api, listener);
}
-int Surface::connect(int api, const sp<IProducerListener>& listener) {
- return connect(api, listener, false);
-}
-
-int Surface::connect(
- int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) {
- if (sListener != nullptr) {
- mListenerProxy = new ProducerListenerProxy(this, sListener);
- }
- return connect(api, mListenerProxy, reportBufferRemoval);
-}
-
-int Surface::connect(
- int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
+int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
ALOGV("Surface::connect");
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
mReportRemovedBuffers = reportBufferRemoval;
- int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
+
+ if (listener != nullptr) {
+ mListenerProxy = new ProducerListenerProxy(this, listener);
+ }
+
+ int err =
+ mGraphicBufferProducer->connect(mListenerProxy, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
mDefaultWidth = output.width;
mDefaultHeight = output.height;
@@ -1898,6 +2103,13 @@
}
mConsumerRunningBehind = (output.numPendingBuffers >= 2);
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ if (listener && listener->needsDeathNotify()) {
+ mSurfaceDeathListener = sp<ProducerDeathListenerProxy>::make(listener);
+ IInterface::asBinder(mGraphicBufferProducer)->linkToDeath(mSurfaceDeathListener);
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
@@ -1911,7 +2123,6 @@
return err;
}
-
int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) {
ATRACE_CALL();
ALOGV("Surface::disconnect");
@@ -1939,6 +2150,14 @@
mConnectedToCpu = false;
}
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ if (mSurfaceDeathListener != nullptr) {
+ IInterface::asBinder(mGraphicBufferProducer)->unlinkToDeath(mSurfaceDeathListener);
+ mSurfaceDeathListener = nullptr;
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
return err;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index af91bb3..df58df4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -20,8 +20,11 @@
#include <stdint.h>
#include <sys/types.h>
+#include <com_android_graphics_libgui_flags.h>
+
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/DisplayState.h>
+#include <android/gui/EdgeExtensionParameters.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/TrustedPresentationThresholds.h>
@@ -40,7 +43,7 @@
#include <system/graphics.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
@@ -86,7 +89,8 @@
return (((int64_t)getpid()) << 32) | ++idCounter;
}
-void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {}
+constexpr int64_t INVALID_VSYNC = -1;
+
} // namespace
const std::string SurfaceComposerClient::kEmpty{};
@@ -207,9 +211,168 @@
return DefaultComposerClient::getComposerClient();
}
+// ---------------------------------------------------------------------------
+
JankDataListener::~JankDataListener() {
}
+status_t JankDataListener::flushJankData() {
+ if (mLayerId == -1) {
+ return INVALID_OPERATION;
+ }
+
+ binder::Status status = ComposerServiceAIDL::getComposerService()->flushJankData(mLayerId);
+ return statusTFromBinderStatus(status);
+}
+
+std::mutex JankDataListenerFanOut::sFanoutInstanceMutex;
+std::unordered_map<int32_t, sp<JankDataListenerFanOut>> JankDataListenerFanOut::sFanoutInstances;
+
+binder::Status JankDataListenerFanOut::onJankData(const std::vector<gui::JankData>& jankData) {
+ // Find the highest VSync ID.
+ int64_t lastVsync = jankData.empty()
+ ? 0
+ : std::max_element(jankData.begin(), jankData.end(),
+ [](const gui::JankData& jd1, const gui::JankData& jd2) {
+ return jd1.frameVsyncId < jd2.frameVsyncId;
+ })
+ ->frameVsyncId;
+
+ // Fan out the jank data callback.
+ std::vector<wp<JankDataListener>> listenersToRemove;
+ for (auto listener : getActiveListeners()) {
+ if (!listener->onJankDataAvailable(jankData) ||
+ (listener->mRemoveAfter >= 0 && listener->mRemoveAfter <= lastVsync)) {
+ listenersToRemove.push_back(listener);
+ }
+ }
+
+ return removeListeners(listenersToRemove)
+ ? binder::Status::ok()
+ : binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER);
+}
+
+status_t JankDataListenerFanOut::addListener(sp<SurfaceControl> sc, sp<JankDataListener> listener) {
+ sp<IBinder> layer = sc->getHandle();
+ if (layer == nullptr) {
+ return UNEXPECTED_NULL;
+ }
+ int32_t layerId = sc->getLayerId();
+
+ sFanoutInstanceMutex.lock();
+ auto it = sFanoutInstances.find(layerId);
+ bool registerNeeded = it == sFanoutInstances.end();
+ sp<JankDataListenerFanOut> fanout;
+ if (registerNeeded) {
+ fanout = sp<JankDataListenerFanOut>::make(layerId);
+ sFanoutInstances.insert({layerId, fanout});
+ } else {
+ fanout = it->second;
+ }
+
+ fanout->mMutex.lock();
+ fanout->mListeners.insert(listener);
+ fanout->mMutex.unlock();
+
+ sFanoutInstanceMutex.unlock();
+
+ if (registerNeeded) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->addJankListener(layer, fanout);
+ return statusTFromBinderStatus(status);
+ }
+ return OK;
+}
+
+status_t JankDataListenerFanOut::removeListener(sp<JankDataListener> listener) {
+ int32_t layerId = listener->mLayerId;
+ if (layerId == -1) {
+ return INVALID_OPERATION;
+ }
+
+ int64_t removeAfter = INVALID_VSYNC;
+ sp<JankDataListenerFanOut> fanout;
+
+ sFanoutInstanceMutex.lock();
+ auto it = sFanoutInstances.find(layerId);
+ if (it != sFanoutInstances.end()) {
+ fanout = it->second;
+ removeAfter = fanout->updateAndGetRemovalVSync();
+ }
+
+ if (removeAfter != INVALID_VSYNC) {
+ // Remove this instance from the map, so that no new listeners are added
+ // while we're scheduled to be removed.
+ sFanoutInstances.erase(layerId);
+ }
+ sFanoutInstanceMutex.unlock();
+
+ if (removeAfter < 0) {
+ return OK;
+ }
+
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->removeJankListener(layerId, fanout,
+ removeAfter);
+ return statusTFromBinderStatus(status);
+}
+
+std::vector<sp<JankDataListener>> JankDataListenerFanOut::getActiveListeners() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+
+ std::vector<sp<JankDataListener>> listeners;
+ for (auto it = mListeners.begin(); it != mListeners.end();) {
+ auto listener = it->promote();
+ if (!listener) {
+ it = mListeners.erase(it);
+ } else {
+ listeners.push_back(std::move(listener));
+ it++;
+ }
+ }
+ return listeners;
+}
+
+bool JankDataListenerFanOut::removeListeners(const std::vector<wp<JankDataListener>>& listeners) {
+ std::scoped_lock<std::mutex> fanoutLock(sFanoutInstanceMutex);
+ std::scoped_lock<std::mutex> listenersLock(mMutex);
+
+ for (auto listener : listeners) {
+ mListeners.erase(listener);
+ }
+
+ if (mListeners.empty()) {
+ sFanoutInstances.erase(mLayerId);
+ return false;
+ }
+ return true;
+}
+
+int64_t JankDataListenerFanOut::updateAndGetRemovalVSync() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (mRemoveAfter >= 0) {
+ // We've already been scheduled to be removed. Don't schedule again.
+ return INVALID_VSYNC;
+ }
+
+ int64_t removeAfter = 0;
+ for (auto it = mListeners.begin(); it != mListeners.end();) {
+ auto listener = it->promote();
+ if (!listener) {
+ it = mListeners.erase(it);
+ } else if (listener->mRemoveAfter < 0) {
+ // We have at least one listener that's still interested. Don't remove.
+ return INVALID_VSYNC;
+ } else {
+ removeAfter = std::max(removeAfter, listener->mRemoveAfter);
+ it++;
+ }
+ }
+
+ mRemoveAfter = removeAfter;
+ return removeAfter;
+}
+
// ---------------------------------------------------------------------------
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -256,14 +419,6 @@
surfaceControls,
CallbackId::Type callbackType) {
std::lock_guard<std::mutex> lock(mMutex);
- return addCallbackFunctionLocked(callbackFunction, surfaceControls, callbackType);
-}
-
-CallbackId TransactionCompletedListener::addCallbackFunctionLocked(
- const TransactionCompletedCallback& callbackFunction,
- const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls,
- CallbackId::Type callbackType) {
startListeningLocked();
CallbackId callbackId(getNextIdLocked(), callbackType);
@@ -272,33 +427,11 @@
for (const auto& surfaceControl : surfaceControls) {
callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
-
- if (callbackType == CallbackId::Type::ON_COMPLETE &&
- mJankListeners.count(surfaceControl->getLayerId()) != 0) {
- callbackId.includeJankData = true;
- }
}
return callbackId;
}
-void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
- sp<SurfaceControl> surfaceControl) {
- std::lock_guard<std::mutex> lock(mMutex);
- mJankListeners.insert({surfaceControl->getLayerId(), listener});
-}
-
-void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
- std::lock_guard<std::mutex> lock(mMutex);
- for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
- if (it->second == listener) {
- it = mJankListeners.erase(it);
- } else {
- it++;
- }
- }
-}
-
void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId,
ReleaseBufferCallback listener) {
std::scoped_lock<std::mutex> lock(mMutex);
@@ -325,32 +458,20 @@
}
void TransactionCompletedListener::addSurfaceControlToCallbacks(
- SurfaceComposerClient::CallbackInfo& callbackInfo,
- const sp<SurfaceControl>& surfaceControl) {
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
std::lock_guard<std::mutex> lock(mMutex);
- bool includingJankData = false;
- for (auto callbackId : callbackInfo.callbackIds) {
+ for (auto callbackId : callbackIds) {
mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
std::forward_as_tuple(
surfaceControl->getHandle()),
std::forward_as_tuple(surfaceControl));
- includingJankData = includingJankData || callbackId.includeJankData;
- }
-
- // If no registered callback is requesting jank data, but there is a jank listener registered
- // on the new surface control, add a synthetic callback that requests the jank data.
- if (!includingJankData && mJankListeners.count(surfaceControl->getLayerId()) != 0) {
- CallbackId callbackId =
- addCallbackFunctionLocked(&emptyCallback, callbackInfo.surfaceControls,
- CallbackId::Type::ON_COMPLETE);
- callbackInfo.callbackIds.emplace(callbackId);
}
}
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
- std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -366,7 +487,6 @@
* sp<SurfaceControl> that could possibly exist for the callbacks.
*/
callbacksMap = mCallbacks;
- jankListenersMap = mJankListeners;
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto& callbackId : transactionStats.callbackIds) {
mCallbacks.erase(callbackId);
@@ -486,12 +606,6 @@
transactionStats.presentFence, surfaceStats);
}
}
-
- if (surfaceStats.jankData.empty()) continue;
- auto jankRange = jankListenersMap.equal_range(layerId);
- for (auto it = jankRange.first; it != jankRange.second; it++) {
- it->second->onJankDataAvailable(surfaceStats.jankData);
- }
}
}
}
@@ -713,7 +827,6 @@
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
: mId(other.mId),
- mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeupStart(other.mEarlyWakeupStart),
mEarlyWakeupEnd(other.mEarlyWakeupEnd),
@@ -753,7 +866,6 @@
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint64_t transactionId = parcel->readUint64();
- const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
const bool earlyWakeupStart = parcel->readBool();
const bool earlyWakeupEnd = parcel->readBool();
@@ -850,7 +962,6 @@
// Parsing was successful. Update the object.
mId = transactionId;
- mTransactionNestCount = transactionNestCount;
mAnimation = animation;
mEarlyWakeupStart = earlyWakeupStart;
mEarlyWakeupEnd = earlyWakeupEnd;
@@ -882,7 +993,6 @@
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
parcel->writeUint64(mId);
- parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
parcel->writeBool(mEarlyWakeupStart);
parcel->writeBool(mEarlyWakeupEnd);
@@ -1004,8 +1114,9 @@
// register all surface controls for all callbackIds for this listener that is merging
for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
- mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo,
- surfaceControl);
+ mTransactionCompletedListener
+ ->addSurfaceControlToCallbacks(surfaceControl,
+ currentProcessCallbackInfo.callbackIds);
}
}
@@ -1033,7 +1144,6 @@
mInputWindowCommands.clear();
mUncacheBuffers.clear();
mMayContainBuffer = false;
- mTransactionNestCount = 0;
mAnimation = false;
mEarlyWakeupStart = false;
mEarlyWakeupEnd = false;
@@ -1059,7 +1169,8 @@
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
Vector<ComposerState> composerStates;
- status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+ Vector<DisplayState> displayStates;
+ status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
ISurfaceComposer::eOneWay,
Transaction::getDefaultApplyToken(), {}, systemTime(),
true, {uncacheBuffer}, false, {}, generateId(), {});
@@ -1361,7 +1472,7 @@
auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
callbackInfo.surfaceControls.insert(sc);
- mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc);
+ mTransactionCompletedListener->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
@@ -1940,8 +2051,9 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext,
CallbackId::Type callbackType) {
- auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
- std::placeholders::_2, std::placeholders::_3);
+ auto callbackWithContext =
+ std::bind(std::move(callback), callbackContext, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
const auto& surfaceControls = mListenerCallbacks[mTransactionCompletedListener].surfaceControls;
CallbackId callbackId =
@@ -1955,13 +2067,15 @@
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
- return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMPLETE);
+ return addTransactionCallback(std::move(callback), callbackContext,
+ CallbackId::Type::ON_COMPLETE);
}
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addTransactionCommittedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
- return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMMIT);
+ return addTransactionCallback(std::move(callback), callbackContext,
+ CallbackId::Type::ON_COMMIT);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
@@ -2217,6 +2331,23 @@
return *this;
}
+bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() {
+ return com::android::graphics::libgui::flags::edge_extension_shader();
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect(
+ const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eEdgeExtensionChanged;
+ s->edgeExtensionParameters = effect;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop(
const sp<SurfaceControl>& sc, const Rect& bufferCrop) {
layer_state_t* s = getLayerState(sc);
@@ -2262,6 +2393,22 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferReleaseChannel(
+ const sp<SurfaceControl>& sc,
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBufferReleaseChannelChanged;
+ s->bufferReleaseChannel = channel;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index c5f9c38..f126c0b 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -139,9 +139,9 @@
uint32_t ignore;
auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
ISurfaceComposerClient::eOpaque);
- mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
+ mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat,
flags, mHandle, {}, &ignore);
- mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);
+ mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat);
// This surface is always consumed by SurfaceFlinger, so the
// producerControlledByApp value doesn't matter; using false.
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 0929b8e..91c9a85 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -15,7 +15,7 @@
*/
#include <android/gui/ISurfaceComposer.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/WindowInfosListenerReporter.h>
#include "gui/WindowInfosUpdate.h"
diff --git a/libs/gui/aidl/Android.bp b/libs/gui/aidl/Android.bp
index 8ed08c2..fd035f6 100644
--- a/libs/gui/aidl/Android.bp
+++ b/libs/gui/aidl/Android.bp
@@ -28,9 +28,6 @@
":libgui_extra_unstructured_aidl_files",
"android/gui/BitTube.aidl",
- "android/gui/CaptureArgs.aidl",
- "android/gui/DisplayCaptureArgs.aidl",
- "android/gui/LayerCaptureArgs.aidl",
"android/gui/LayerMetadata.aidl",
"android/gui/ParcelableVsyncEventData.aidl",
"android/gui/ScreenCaptureResults.aidl",
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
index 9f198ca..4920344 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -16,4 +16,63 @@
package android.gui;
-parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::CaptureArgs";
+import android.gui.ARect;
+
+// Common arguments for capturing content on-screen
+parcelable CaptureArgs {
+ const int UNSET_UID = -1;
+
+ // Desired pixel format of the final screenshotted buffer
+ int /*ui::PixelFormat*/ pixelFormat = 1;
+
+ // Crop in layer space: all content outside of the crop will not be captured.
+ ARect sourceCrop;
+
+ // Scale in the x-direction for the screenshotted result.
+ float frameScaleX = 1.0f;
+
+ // Scale in the y-direction for the screenshotted result.
+ float frameScaleY = 1.0f;
+
+ // True if capturing secure layers is permitted
+ boolean captureSecureLayers = false;
+
+ // UID whose content we want to screenshot
+ int uid = UNSET_UID;
+
+ // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+ // result will be in a colorspace appropriate for capturing the display contents
+ // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+ // different from SRGB (byte per color), and failed when checking colors in tests.
+ // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+ int /*ui::Dataspace*/ dataspace = 0;
+
+ // The receiver of the capture can handle protected buffer. A protected buffer has
+ // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+ // Any read/write access from unprotected context will result in undefined behaviour.
+ // Protected contents are typically DRM contents. This has no direct implication to the
+ // secure property of the surface, which is specified by the application explicitly to avoid
+ // the contents being accessed/captured by screenshot or unsecure display.
+ boolean allowProtected = false;
+
+ // True if the content should be captured in grayscale
+ boolean grayscale = false;
+
+ // List of layers to exclude capturing from
+ IBinder[] excludeHandles;
+
+ // Hint that the caller will use the screenshot animation as part of a transition animation.
+ // The canonical example would be screen rotation - in such a case any color shift in the
+ // screenshot is a detractor so composition in the display's colorspace is required.
+ // Otherwise, the system may choose a colorspace that is more appropriate for use-cases
+ // such as file encoding or for blending HDR content into an ap's UI, where the display's
+ // exact colorspace is not an appropriate intermediate result.
+ // Note that if the caller is requesting a specific dataspace, this hint does nothing.
+ boolean hintForSeamlessTransition = false;
+
+ // Allows the screenshot to attach a gainmap, which allows for a per-pixel
+ // transformation of the screenshot to another luminance range, typically
+ // mapping an SDR base image into HDR.
+ boolean attachGainmap = false;
+}
+
diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
index fc97dbf..e00a2df 100644
--- a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
@@ -16,5 +16,18 @@
package android.gui;
-parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::DisplayCaptureArgs";
+import android.gui.CaptureArgs;
+
+// Arguments for screenshotting an entire display
+parcelable DisplayCaptureArgs {
+ CaptureArgs captureArgs;
+
+ // The display that we want to screenshot
+ IBinder displayToken;
+
+ // The width of the render area when we screenshot
+ int width = 0;
+ // The length of the render area when we screenshot
+ int height = 0;
+}
diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl
similarity index 70%
copy from libs/tracing_perfetto/include/trace_result.h
copy to libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl
index f7581fc..44f4259 100644
--- a/libs/tracing_perfetto/include/trace_result.h
+++ b/libs/gui/aidl/android/gui/EdgeExtensionParameters.aidl
@@ -14,17 +14,14 @@
* limitations under the License.
*/
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
+package android.gui;
-namespace tracing_perfetto {
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
-};
-
-}
-
-#endif // TRACE_RESULT_H
+/** @hide */
+parcelable EdgeExtensionParameters {
+ // These represent the translation of the window as requested by the animation
+ boolean extendRight;
+ boolean extendLeft;
+ boolean extendTop;
+ boolean extendBottom;
+}
\ No newline at end of file
diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/gui/aidl/android/gui/IJankListener.aidl
similarity index 68%
copy from libs/tracing_perfetto/include/trace_result.h
copy to libs/gui/aidl/android/gui/IJankListener.aidl
index f7581fc..2bfd1af 100644
--- a/libs/tracing_perfetto/include/trace_result.h
+++ b/libs/gui/aidl/android/gui/IJankListener.aidl
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
+package android.gui;
-namespace tracing_perfetto {
+import android.gui.JankData;
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
-};
+/** @hide */
+interface IJankListener {
+ /**
+ * Callback reporting jank data of the most recent frames.
+ * @See {@link ISurfaceComposer#addJankListener(IBinder, IJankListener)}
+ */
+ void onJankData(in JankData[] data);
}
-
-#endif // TRACE_RESULT_H
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 6d018ea..ac14138 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -42,6 +42,7 @@
import android.gui.ITunnelModeEnabledListener;
import android.gui.IWindowInfosListener;
import android.gui.IWindowInfosPublisher;
+import android.gui.IJankListener;
import android.gui.LayerCaptureArgs;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
@@ -580,4 +581,22 @@
* This method should not block the ShutdownThread therefore it's handled asynchronously.
*/
oneway void notifyShutdown();
+
+ /**
+ * Registers the jank listener on the given layer to receive jank data of future frames.
+ */
+ void addJankListener(IBinder layer, IJankListener listener);
+
+ /**
+ * Flushes any pending jank data on the given layer to any registered listeners on that layer.
+ */
+ oneway void flushJankData(int layerId);
+
+ /**
+ * Schedules the removal of the jank listener from the given layer after the VSync with the
+ * specified ID. Use a value <= 0 for afterVsync to remove the listener immediately. The given
+ * listener will not be removed before the given VSync, but may still receive data for frames
+ * past the provided VSync.
+ */
+ oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync);
}
diff --git a/libs/gui/aidl/android/gui/JankData.aidl b/libs/gui/aidl/android/gui/JankData.aidl
new file mode 100644
index 0000000..ec13681
--- /dev/null
+++ b/libs/gui/aidl/android/gui/JankData.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 android.gui;
+
+ /** @hide */
+parcelable JankData {
+ /**
+ * Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
+ */
+ long frameVsyncId;
+
+ /**
+ * Bitmask of jank types that occurred.
+ */
+ int jankType;
+
+ /**
+ * Time between frames in nanoseconds.
+ */
+ long frameIntervalNs;
+
+ /**
+ * Time allocated to the application to render this frame.
+ */
+ long scheduledAppFrameTimeNs;
+
+ /**
+ * Time taken by the application to render this frame.
+ */
+ long actualAppFrameTimeNs;
+}
diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
index 18d293f..004c35a 100644
--- a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
@@ -16,4 +16,15 @@
package android.gui;
-parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h" rust_type "gui_aidl_types_rs::LayerCaptureArgs";
+import android.gui.CaptureArgs;
+
+// Arguments for capturing a layer and/or its children
+parcelable LayerCaptureArgs {
+ CaptureArgs captureArgs;
+
+ // The Layer that we may want to capture. We would also capture its children
+ IBinder layerHandle;
+ // True if we don't actually want to capture the layer and want to capture
+ // its children instead.
+ boolean childrenOnly = false;
+}
diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
index 97a9035..f4ef16d 100644
--- a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
+++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
\ No newline at end of file
+parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
diff --git a/libs/gui/include/gui/AidlStatusUtil.h b/libs/gui/include/gui/AidlUtil.h
similarity index 84%
rename from libs/gui/include/gui/AidlStatusUtil.h
rename to libs/gui/include/gui/AidlUtil.h
index 55be27b..a3ecd84 100644
--- a/libs/gui/include/gui/AidlStatusUtil.h
+++ b/libs/gui/include/gui/AidlUtil.h
@@ -16,9 +16,11 @@
#pragma once
+#include <android/gui/ARect.h>
#include <binder/Status.h>
+#include <ui/Rect.h>
-// Extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h
+// Originally extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h
namespace android::gui::aidl_utils {
/**
@@ -68,7 +70,7 @@
*
* return_type method(type0 param0, ...)
*/
-static inline status_t statusTFromBinderStatus(const ::android::binder::Status &status) {
+static inline status_t statusTFromBinderStatus(const ::android::binder::Status& status) {
return status.isOk() ? OK // check OK,
: status.serviceSpecificErrorCode() // service-side error, not standard Java exception
// (fromServiceSpecificError)
@@ -84,8 +86,8 @@
* where Java callers expect an exception, not an integer return value.
*/
static inline ::android::binder::Status binderStatusFromStatusT(
- status_t status, const char *optionalMessage = nullptr) {
- const char *const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
+ status_t status, const char* optionalMessage = nullptr) {
+ const char* const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
// From binder::Status instructions:
// Prefer a generic exception code when possible, then a service specific
// code, and finally a status_t for low level failures or legacy support.
@@ -111,4 +113,26 @@
return Status::fromServiceSpecificError(status, emptyIfNull);
}
+static inline Rect fromARect(ARect rect) {
+ return Rect(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static inline ARect toARect(Rect rect) {
+ ARect aRect;
+
+ aRect.left = rect.left;
+ aRect.top = rect.top;
+ aRect.right = rect.right;
+ aRect.bottom = rect.bottom;
+ return aRect;
+}
+
+static inline ARect toARect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+ return toARect(Rect(left, top, right, bottom));
+}
+
+static inline ARect toARect(int32_t width, int32_t height) {
+ return toARect(Rect(width, height));
+}
+
} // namespace android::gui::aidl_utils
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 0e1a505..8592cff 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-
+#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/SurfaceComposerClient.h>
@@ -28,7 +29,6 @@
#include <utils/RefBase.h>
#include <system/window.h>
-#include <thread>
#include <queue>
#include <com_android_graphics_libgui_flags.h>
@@ -40,12 +40,20 @@
class BLASTBufferItemConsumer : public BufferItemConsumer {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+ : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
+#else
BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
: BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mBLASTBufferQueue(std::move(bbq)),
mCurrentlyConnected(false),
- mPreviouslyConnected(false) {}
+ mPreviouslyConnected(false) {
+ }
void onDisconnect() override EXCLUDES(mMutex);
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -95,15 +103,21 @@
void onFrameDequeued(const uint64_t) override;
void onFrameCancelled(const uint64_t) override;
+ TransactionCompletedCallbackTakesContext makeTransactionCommittedCallbackThunk();
void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
+
+ TransactionCompletedCallbackTakesContext makeTransactionCallbackThunk();
virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
+
+ ReleaseBufferCallback makeReleaseBufferCallbackThunk();
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount);
void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount,
bool fakeRelease) REQUIRES(mMutex);
+
bool syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
bool acquireSingleBuffer = true);
void stopContinuousSyncTransaction();
@@ -128,9 +142,11 @@
* indicates the reason for the hang.
*/
void setTransactionHangCallback(std::function<void(const std::string&)> callback);
-
+ void setApplyToken(sp<IBinder>);
virtual ~BLASTBufferQueue();
+ void onFirstRef() override;
+
private:
friend class BLASTBufferQueueHelper;
friend class BBQBufferQueueProducer;
@@ -170,8 +186,7 @@
// BufferQueue internally allows 1 more than
// the max to be acquired
- int32_t mMaxAcquiredBuffers = 1;
-
+ int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1;
int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0;
int32_t mNumAcquired GUARDED_BY(mMutex) = 0;
@@ -256,7 +271,7 @@
// Queues up transactions using this token in SurfaceFlinger. This prevents queued up
// transactions from other parts of the client from blocking this transaction.
- const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make();
+ sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make();
// Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or
// we will deadlock.
@@ -300,6 +315,51 @@
std::function<void(const std::string&)> mTransactionHangCallback;
std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ class BufferReleaseReader {
+ public:
+ BufferReleaseReader() = default;
+ BufferReleaseReader(std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint>);
+ BufferReleaseReader& operator=(BufferReleaseReader&&);
+
+ // Block until we can read a buffer release message.
+ //
+ // Returns:
+ // * OK if a ReleaseCallbackId and Fence were successfully read.
+ // * WOULD_BLOCK if the blocking read was interrupted by interruptBlockingRead.
+ // * UNKNOWN_ERROR if something went wrong.
+ status_t readBlocking(ReleaseCallbackId& outId, sp<Fence>& outReleaseFence,
+ uint32_t& outMaxAcquiredBufferCount);
+
+ // Signals the reader's eventfd to wake up any threads waiting on readBlocking.
+ void interruptBlockingRead();
+
+ private:
+ std::mutex mMutex;
+ std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mEndpoint GUARDED_BY(mMutex);
+ android::base::unique_fd mEpollFd;
+ android::base::unique_fd mEventFd;
+ };
+
+ // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to
+ // the client. See BBQBufferQueueProducer::dequeueBuffer for details.
+ std::shared_ptr<BufferReleaseReader> mBufferReleaseReader;
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+
+ class BufferReleaseThread {
+ public:
+ BufferReleaseThread() = default;
+ ~BufferReleaseThread();
+ void start(const sp<BLASTBufferQueue>&);
+
+ private:
+ std::shared_ptr<std::atomic_bool> mRunning;
+ std::shared_ptr<BufferReleaseReader> mReader;
+ };
+
+ BufferReleaseThread mBufferReleaseThread;
+#endif
};
} // namespace android
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index a905610..6810eda 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -17,13 +17,15 @@
#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H
#define ANDROID_GUI_BUFFERITEMCONSUMER_H
-#include <gui/ConsumerBase.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueue.h>
+#include <gui/ConsumerBase.h>
#define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer"
namespace android {
+class GraphicBuffer;
class String8;
/**
@@ -51,9 +53,17 @@
// access at the same time.
// controlledByApp tells whether this consumer is controlled by the
// application.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferItemConsumer(uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+ BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer,
uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
~BufferItemConsumer() override;
@@ -85,7 +95,25 @@
status_t releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence = Fence::NO_FENCE);
- private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ status_t releaseBuffer(const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence = Fence::NO_FENCE);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+protected:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This should only be used by BLASTBufferQueue:
+ BufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+private:
+ void initialize(uint64_t consumerUsage, int bufferCount);
+
+ status_t releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence);
+
void freeBufferLocked(int slotIndex) override;
// mBufferFreedListener is the listener object that will be called when
diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h
new file mode 100644
index 0000000..51fe0b6
--- /dev/null
+++ b/libs/gui/include/gui/BufferReleaseChannel.h
@@ -0,0 +1,125 @@
+/*
+ * 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 <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include <binder/Parcelable.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+#include <utils/Errors.h>
+
+namespace android::gui {
+
+/**
+ * IPC wrapper to pass release fences from SurfaceFlinger to apps via a local unix domain socket.
+ */
+class BufferReleaseChannel {
+private:
+ class Endpoint {
+ public:
+ Endpoint(std::string name, android::base::unique_fd fd)
+ : mName(std::move(name)), mFd(std::move(fd)) {}
+ Endpoint() {}
+
+ Endpoint(Endpoint&&) noexcept = default;
+ Endpoint& operator=(Endpoint&&) noexcept = default;
+
+ Endpoint(const Endpoint&) = delete;
+ void operator=(const Endpoint&) = delete;
+
+ const android::base::unique_fd& getFd() const { return mFd; }
+
+ protected:
+ std::string mName;
+ android::base::unique_fd mFd;
+ };
+
+public:
+ class ConsumerEndpoint : public Endpoint {
+ public:
+ ConsumerEndpoint(std::string name, android::base::unique_fd fd)
+ : Endpoint(std::move(name), std::move(fd)) {}
+
+ /**
+ * Reads a release fence from the BufferReleaseChannel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no fence present.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t readReleaseFence(ReleaseCallbackId& outReleaseCallbackId,
+ sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount);
+
+ private:
+ std::vector<uint8_t> mFlattenedBuffer;
+ };
+
+ class ProducerEndpoint : public Endpoint, public Parcelable {
+ public:
+ ProducerEndpoint(std::string name, android::base::unique_fd fd)
+ : Endpoint(std::move(name), std::move(fd)) {}
+ ProducerEndpoint() {}
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+ status_t writeReleaseFence(const ReleaseCallbackId&, const sp<Fence>& releaseFence,
+ uint32_t maxAcquiredBufferCount);
+
+ private:
+ std::vector<uint8_t> mFlattenedBuffer;
+ };
+
+ /**
+ * Create two endpoints that make up the BufferReleaseChannel.
+ *
+ * Return OK on success.
+ */
+ static status_t open(const std::string name, std::unique_ptr<ConsumerEndpoint>& outConsumer,
+ std::shared_ptr<ProducerEndpoint>& outProducer);
+
+ struct Message : public Flattenable<Message> {
+ ReleaseCallbackId releaseCallbackId;
+ sp<Fence> releaseFence = Fence::NO_FENCE;
+ uint32_t maxAcquiredBufferCount;
+
+ Message() = default;
+ Message(ReleaseCallbackId releaseCallbackId, sp<Fence> releaseFence,
+ uint32_t maxAcquiredBufferCount)
+ : releaseCallbackId{releaseCallbackId},
+ releaseFence{std::move(releaseFence)},
+ maxAcquiredBufferCount{maxAcquiredBufferCount} {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const;
+
+ size_t getFdCount() const { return releaseFence->getFdCount(); }
+
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ private:
+ size_t getPodSize() const;
+ };
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 8ff0cd0..e976aa4 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -17,18 +17,17 @@
#ifndef ANDROID_GUI_CONSUMERBASE_H
#define ANDROID_GUI_CONSUMERBASE_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/OccupancyTracker.h>
-
#include <ui/PixelFormat.h>
-
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/threads.h>
-
namespace android {
// ----------------------------------------------------------------------------
@@ -76,12 +75,28 @@
void dumpState(String8& result) const;
void dumpState(String8& result, const char* prefix) const;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // Returns a Surface that can be used as the producer for this consumer.
+ sp<Surface> getSurface() const;
+
+ // DEPRECATED, DO NOT USE. Returns the underlying IGraphicBufferConsumer
+ // that backs this ConsumerBase.
+ sp<IGraphicBufferConsumer> getIGraphicBufferConsumer() const
+ __attribute((deprecated("DO NOT USE: Temporary hack for refactoring")));
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
// See IGraphicBufferConsumer::detachBuffer
- status_t detachBuffer(int slot);
+ status_t detachBuffer(int slot) __attribute((
+ deprecated("Please use the GraphicBuffer variant--slots are deprecated.")));
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // See IGraphicBufferConsumer::detachBuffer
+ status_t detachBuffer(const sp<GraphicBuffer>& buffer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
// See IGraphicBufferConsumer::setDefaultBufferSize
status_t setDefaultBufferSize(uint32_t width, uint32_t height);
@@ -98,9 +113,18 @@
// See IGraphicBufferConsumer::setTransformHint
status_t setTransformHint(uint32_t hint);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // See IGraphicBufferConsumer::setMaxBufferCount
+ status_t setMaxBufferCount(int bufferCount);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// See IGraphicBufferConsumer::setMaxAcquiredBufferCount
status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ status_t setConsumerIsProtected(bool isProtected);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// See IGraphicBufferConsumer::getSidebandStream
sp<NativeHandle> getSidebandStream() const;
@@ -115,12 +139,24 @@
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
+ void initialize(bool controlledByApp);
+
protected:
// ConsumerBase constructs a new ConsumerBase object to consume image
// buffers from the given IGraphicBufferConsumer.
// The controlledByApp flag indicates that this consumer is under the application's
// control.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ explicit ConsumerBase(bool controlledByApp = false, bool consumerIsSurfaceFlinger = false);
+ explicit ConsumerBase(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+
+ explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false)
+ __attribute((deprecated("ConsumerBase should own its own producer, and constructing it "
+ "without one is fragile! This method is going away soon.")));
+#else
explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// onLastStrongRef gets called by RefBase just before the dtor of the most
// derived class. It is used to clean up the buffers so that ConsumerBase
@@ -150,6 +186,10 @@
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
+ virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
+
+ virtual status_t detachBufferLocked(int slotIndex);
+
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
// slot. Otherwise it has no effect.
@@ -264,10 +304,16 @@
Mutex mFrameAvailableMutex;
wp<FrameAvailableListener> mFrameAvailableListener;
- // The ConsumerBase has-a BufferQueue and is responsible for creating this object
- // if none is supplied
+ // The ConsumerBase has-a BufferQueue and is responsible for creating these
+ // objects if not supplied.
sp<IGraphicBufferConsumer> mConsumer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This Surface wraps the IGraphicBufferConsumer created for this
+ // ConsumerBase.
+ sp<Surface> mSurface;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// The final release fence of the most recent buffer released by
// releaseBufferLocked.
sp<Fence> mPrevFinalReleaseFence;
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 806fbe8..2bba61b 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -19,8 +19,9 @@
#include <system/window.h>
-#include <gui/ConsumerBase.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueue.h>
+#include <gui/ConsumerBase.h>
#include <utils/Vector.h>
@@ -91,8 +92,17 @@
// Create a new CPU consumer. The maxLockedBuffers parameter specifies
// how many buffers can be locked for user access at the same time.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false,
+ bool isConsumerSurfaceFlinger = false);
+
+ CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
size_t maxLockedBuffers, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// Gets the next graphics buffer from the producer and locks it for CPU use,
// filling out the passed-in locked buffer structure with the native pointer
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
deleted file mode 100644
index e29ce41..0000000
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <gui/SpHash.h>
-#include <ui/GraphicTypes.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <unordered_set>
-
-namespace android::gui {
-
-struct CaptureArgs : public Parcelable {
- const static int32_t UNSET_UID = -1;
- virtual ~CaptureArgs() = default;
-
- ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
- Rect sourceCrop;
- float frameScaleX{1};
- float frameScaleY{1};
- bool captureSecureLayers{false};
- int32_t uid{UNSET_UID};
- // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
- // result will be in a colorspace appropriate for capturing the display contents
- // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
- // different from SRGB (byte per color), and failed when checking colors in tests.
- // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
- ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
-
- // The receiver of the capture can handle protected buffer. A protected buffer has
- // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
- // Any read/write access from unprotected context will result in undefined behaviour.
- // Protected contents are typically DRM contents. This has no direct implication to the
- // secure property of the surface, which is specified by the application explicitly to avoid
- // the contents being accessed/captured by screenshot or unsecure display.
- bool allowProtected = false;
-
- bool grayscale = false;
-
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
-
- // Hint that the caller will use the screenshot animation as part of a transition animation.
- // The canonical example would be screen rotation - in such a case any color shift in the
- // screenshot is a detractor so composition in the display's colorspace is required.
- // Otherwise, the system may choose a colorspace that is more appropriate for use-cases
- // such as file encoding or for blending HDR content into an ap's UI, where the display's
- // exact colorspace is not an appropriate intermediate result.
- // Note that if the caller is requesting a specific dataspace, this hint does nothing.
- bool hintForSeamlessTransition = false;
-
- virtual status_t writeToParcel(Parcel* output) const;
- virtual status_t readFromParcel(const Parcel* input);
-};
-
-struct DisplayCaptureArgs : CaptureArgs {
- sp<IBinder> displayToken;
- uint32_t width{0};
- uint32_t height{0};
-
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-};
-
-}; // namespace android::gui
diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/gui/include/gui/Flags.h
similarity index 63%
copy from libs/tracing_perfetto/include/trace_result.h
copy to libs/gui/include/gui/Flags.h
index f7581fc..735375a 100644
--- a/libs/tracing_perfetto/include/trace_result.h
+++ b/libs/gui/include/gui/Flags.h
@@ -14,17 +14,11 @@
* limitations under the License.
*/
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
+#pragma once
-namespace tracing_perfetto {
+#include <com_android_graphics_libgui_flags.h>
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
-};
-
-}
-
-#endif // TRACE_RESULT_H
+#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \
+ (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index 3d1be4d..462081b 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -116,7 +116,7 @@
// Public for testing.
static nsecs_t snapToNextTick(
nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
- nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; };
+ nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; }
nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index ba268ab..bfe3eb3 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
@@ -82,12 +83,25 @@
// If the constructor without the tex parameter is used, the GLConsumer is
// created in a detached state, and attachToContext must be called before
// calls to updateTexImage.
- GLConsumer(const sp<IGraphicBufferConsumer>& bq,
- uint32_t tex, uint32_t texureTarget, bool useFenceSync,
- bool isControlledByApp);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
- GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget,
- bool useFenceSync, bool isControlledByApp);
+ GLConsumer(uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// updateTexImage acquires the most recently queued buffer, and sets the
// image contents of the target texture to it.
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 84a1730..197e792 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -867,6 +867,6 @@
#endif
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index eb4a802..9a422fd 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -18,6 +18,7 @@
#include <android/gui/CachingHint.h>
#include <android/gui/DisplayBrightness.h>
+#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/FrameTimelineInfo.h>
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/IFpsListener.h>
@@ -27,6 +28,7 @@
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/IWindowInfosPublisher.h>
+#include <android/gui/LayerCaptureArgs.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
#include <gui/ITransactionCompletedListener.h>
@@ -70,13 +72,6 @@
using gui::IScreenCaptureListener;
using gui::SpHash;
-namespace gui {
-
-struct DisplayCaptureArgs;
-struct LayerCaptureArgs;
-
-} // namespace gui
-
namespace ui {
struct DisplayMode;
@@ -112,7 +107,7 @@
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index bc97cd0..014029b 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -16,8 +16,6 @@
#pragma once
-#include "JankInfo.h"
-
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
@@ -40,15 +38,10 @@
class CallbackId : public Parcelable {
public:
int64_t id;
- enum class Type : int32_t {
- ON_COMPLETE = 0,
- ON_COMMIT = 1,
- /*reserved for serialization = 2*/
- } type;
- bool includeJankData; // Only respected for ON_COMPLETE callbacks.
+ enum class Type : int32_t { ON_COMPLETE = 0, ON_COMMIT = 1 } type;
CallbackId() {}
- CallbackId(int64_t id, Type type) : id(id), type(type), includeJankData(false) {}
+ CallbackId(int64_t id, Type type) : id(id), type(type) {}
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
@@ -113,29 +106,6 @@
nsecs_t dequeueReadyTime;
};
-/**
- * Jank information representing SurfaceFlinger's jank classification about frames for a specific
- * surface.
- */
-class JankData : public Parcelable {
-public:
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-
- JankData();
- JankData(int64_t frameVsyncId, int32_t jankType, nsecs_t frameIntervalNs)
- : frameVsyncId(frameVsyncId), jankType(jankType), frameIntervalNs(frameIntervalNs) {}
-
- // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
- int64_t frameVsyncId;
-
- // Bitmask of janks that occurred
- int32_t jankType;
-
- // Expected duration of the frame
- nsecs_t frameIntervalNs;
-};
-
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -145,14 +115,13 @@
SurfaceStats(const sp<IBinder>& sc, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
const sp<Fence>& prevReleaseFence, std::optional<uint32_t> hint,
uint32_t currentMaxAcquiredBuffersCount, FrameEventHistoryStats frameEventStats,
- std::vector<JankData> jankData, ReleaseCallbackId previousReleaseCallbackId)
+ ReleaseCallbackId previousReleaseCallbackId)
: surfaceControl(sc),
acquireTimeOrFence(std::move(acquireTimeOrFence)),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
currentMaxAcquiredBufferCount(currentMaxAcquiredBuffersCount),
eventStats(frameEventStats),
- jankData(std::move(jankData)),
previousReleaseCallbackId(previousReleaseCallbackId) {}
sp<IBinder> surfaceControl;
@@ -161,7 +130,6 @@
std::optional<uint32_t> transformHint = 0;
uint32_t currentMaxAcquiredBufferCount = 0;
FrameEventHistoryStats eventStats;
- std::vector<JankData> jankData;
ReleaseCallbackId previousReleaseCallbackId;
};
diff --git a/libs/gui/include/gui/LayerCaptureArgs.h b/libs/gui/include/gui/LayerCaptureArgs.h
deleted file mode 100644
index fae2bcc..0000000
--- a/libs/gui/include/gui/LayerCaptureArgs.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <gui/DisplayCaptureArgs.h>
-
-namespace android::gui {
-
-struct LayerCaptureArgs : CaptureArgs {
- sp<IBinder> layerHandle;
- bool childrenOnly{false};
-
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-};
-
-}; // namespace android::gui
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 9cf62bc..7ee291d 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -74,6 +74,7 @@
} // namespace android::gui
using android::gui::METADATA_ACCESSIBILITY_ID;
+using android::gui::METADATA_CALLING_UID;
using android::gui::METADATA_DEQUEUE_TIME;
using android::gui::METADATA_GAME_MODE;
using android::gui::METADATA_MOUSE_CURSOR;
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 5f2f8dc..2cdde32 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,7 +21,9 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/IWindowInfosReportedListener.h>
+#include <android/gui/LayerCaptureArgs.h>
#include <android/gui/TrustedPresentationThresholds.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
@@ -29,13 +31,13 @@
#include <math/mat4.h>
#include <android/gui/DropInputMode.h>
+#include <android/gui/EdgeExtensionParameters.h>
#include <android/gui/FocusRequest.h>
#include <android/gui/TrustedOverlay.h>
#include <ftl/flags.h>
-#include <gui/DisplayCaptureArgs.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
@@ -218,6 +220,8 @@
eTrustedOverlayChanged = 0x4000'00000000,
eDropInputModeChanged = 0x8000'00000000,
eExtendedRangeBrightnessChanged = 0x10000'00000000,
+ eEdgeExtensionChanged = 0x20000'00000000,
+ eBufferReleaseChannelChanged = 0x40000'00000000,
};
layer_state_t();
@@ -241,7 +245,7 @@
layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
layer_state_t::eTransformToDisplayInverseChanged |
- layer_state_t::eTransparentRegionChanged;
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged;
// Buffer and related updates.
static constexpr uint64_t BUFFER_CHANGES = layer_state_t::eApiChanged |
@@ -393,6 +397,9 @@
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
+ // Edge extension effect to be applied to this layer
+ gui::EdgeExtensionParameters edgeExtensionParameters;
+
Rect bufferCrop;
Rect destinationFrame;
@@ -407,6 +414,8 @@
TrustedPresentationThresholds trustedPresentationThresholds;
TrustedPresentationListener trustedPresentationListener;
+
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
};
class ComposerState {
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
index 6e17791..f176f48 100644
--- a/libs/gui/include/gui/ScreenCaptureResults.h
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -36,6 +36,11 @@
bool capturedSecureLayers{false};
bool capturedHdrLayers{false};
ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+ // A gainmap that can be used to "lift" the screenshot into HDR
+ sp<GraphicBuffer> optionalGainMap;
+ // HDR/SDR ratio value that fully applies the gainmap.
+ // Note that we use 1/64 epsilon offsets to eliminate precision issues
+ float hdrSdrRatio{1.0f};
};
} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index bdcaaf2..14a3513 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -18,6 +18,7 @@
#define ANDROID_GUI_SURFACE_H
#include <android/gui/FrameTimelineInfo.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/HdrMetadata.h>
#include <gui/IGraphicBufferProducer.h>
@@ -35,6 +36,8 @@
namespace android {
+class GraphicBuffer;
+
namespace gui {
class ISurfaceComposer;
} // namespace gui
@@ -56,8 +59,41 @@
virtual bool needsReleaseNotify() = 0;
virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0;
+ virtual void onBufferDetached(int slot) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual void onBufferAttached() {}
+ virtual bool needsAttachNotify() { return false; }
+#endif
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // Called if this Surface is connected to a remote implementation and it
+ // dies or becomes unavailable.
+ virtual void onRemoteDied() {}
+
+ // Clients will overwrite this if they want to receive a notification
+ // via onRemoteDied. This should return a constant value.
+ virtual bool needsDeathNotify() { return false; }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
};
+class StubSurfaceListener : public SurfaceListener {
+public:
+ virtual ~StubSurfaceListener() {}
+ virtual void onBufferReleased() override {}
+ virtual bool needsReleaseNotify() { return false; }
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {}
+ virtual void onBufferDetached(int /*slot*/) override {}
+};
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+// Contains additional data from the queueBuffer operation.
+struct SurfaceQueueBufferOutput {
+ // True if this queueBuffer caused a buffer to be replaced in the queue
+ // (and therefore not will not be acquired)
+ bool bufferReplaced = false;
+};
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
@@ -154,6 +190,11 @@
*/
virtual void allocateBuffers();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // See IGraphicBufferProducer::allowAllocation
+ status_t allowAllocation(bool allowAllocation);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
/* Sets the generation number on the IGraphicBufferProducer and updates the
* generation number on any buffers attached to the Surface after this call.
* See IGBP::setGenerationNumber for more information. */
@@ -170,6 +211,14 @@
* in <system/window.h>. */
int setScalingMode(int mode);
+ virtual int setBuffersTimestamp(int64_t timestamp);
+ virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
+ virtual int setCrop(Rect const* rect);
+ virtual int setBuffersTransform(uint32_t transform);
+ virtual int setBuffersStickyTransform(uint32_t transform);
+ virtual int setBuffersFormat(PixelFormat format);
+ virtual int setUsage(uint64_t reqUsage);
+
// See IGraphicBufferProducer::setDequeueTimeout
status_t setDequeueTimeout(nsecs_t timeout);
@@ -321,7 +370,12 @@
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd,
+ SurfaceQueueBufferOutput* surfaceOutput = nullptr);
+#else
virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
virtual int perform(int operation, va_list args);
virtual int setSwapInterval(int interval);
@@ -330,16 +384,9 @@
virtual int connect(int api);
virtual int setBufferCount(int bufferCount);
virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
- virtual int setBuffersFormat(PixelFormat format);
- virtual int setBuffersTransform(uint32_t transform);
- virtual int setBuffersStickyTransform(uint32_t transform);
- virtual int setBuffersTimestamp(int64_t timestamp);
- virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
- virtual int setCrop(Rect const* rect);
- virtual int setUsage(uint64_t reqUsage);
virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
public:
@@ -357,22 +404,15 @@
virtual int unlockAndPost();
virtual int query(int what, int* value) const;
- virtual int connect(int api, const sp<IProducerListener>& listener);
-
// When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch
// GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or
// attachBuffer call. This allows clients with their own buffer caches to free up buffers no
// longer in use by this surface.
- virtual int connect(
- int api, const sp<IProducerListener>& listener,
- bool reportBufferRemoval);
- virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
+ virtual int connect(int api, const sp<SurfaceListener>& listener,
+ bool reportBufferRemoval = false);
+ virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
virtual int attachBuffer(ANativeWindowBuffer*);
- virtual int connect(
- int api, bool reportBufferRemoval,
- const sp<SurfaceListener>& sListener);
virtual void destroy();
// When client connects to Surface with reportBufferRemoval set to true, any buffers removed
@@ -387,6 +427,21 @@
static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
ui::Dataspace dataspace);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // Dequeues a buffer and its outFence, which must be signalled before the buffer can be used.
+ status_t dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence);
+
+ // Queues a buffer, with an optional fd fence that captures pending work on the buffer. This
+ // buffer must have been returned by dequeueBuffer or associated with this Surface via an
+ // attachBuffer operation.
+ status_t queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd = Fence::NO_FENCE,
+ SurfaceQueueBufferOutput* output = nullptr);
+
+ // Detaches this buffer, dissociating it from this Surface. This buffer must have been returned
+ // by queueBuffer or associated with this Surface via an attachBuffer operation.
+ status_t detachBuffer(const sp<GraphicBuffer>& buffer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
// Batch version of dequeueBuffer, cancelBuffer and queueBuffer
// Note that these batched operations are not supported when shared buffer mode is being used.
struct BatchBuffer {
@@ -401,8 +456,13 @@
int fenceFd = -1;
nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
};
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ virtual int queueBuffers(const std::vector<BatchQueuedBuffer>& buffers,
+ std::vector<SurfaceQueueBufferOutput>* queueBufferOutputs = nullptr);
+#else
virtual int queueBuffers(
const std::vector<BatchQueuedBuffer>& buffers);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
@@ -422,12 +482,38 @@
return mSurfaceListener->needsReleaseNotify();
}
+ virtual void onBufferDetached(int slot) { mSurfaceListener->onBufferDetached(slot); }
+
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual void onBufferAttached() {
+ mSurfaceListener->onBufferAttached();
+ }
+
+ virtual bool needsAttachNotify() {
+ return mSurfaceListener->needsAttachNotify();
+ }
+#endif
private:
wp<Surface> mParent;
sp<SurfaceListener> mSurfaceListener;
};
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ class ProducerDeathListenerProxy : public IBinder::DeathRecipient {
+ public:
+ ProducerDeathListenerProxy(wp<SurfaceListener> surfaceListener);
+ ProducerDeathListenerProxy(ProducerDeathListenerProxy&) = delete;
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>&) override;
+
+ private:
+ wp<SurfaceListener> mSurfaceListener;
+ };
+ friend class ProducerDeathListenerProxy;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
void querySupportedTimestampsLocked() const;
void freeAllBuffers();
@@ -459,6 +545,13 @@
// TODO: rename to mBufferProducer
sp<IGraphicBufferProducer> mGraphicBufferProducer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // mSurfaceDeathListener gets registered as mGraphicBufferProducer's
+ // DeathRecipient when SurfaceListener::needsDeathNotify returns true and
+ // gets notified when it dies.
+ sp<ProducerDeathListenerProxy> mSurfaceDeathListener;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
// mSlots stores the buffers that have been allocated for each buffer slot.
// It is initialized to null pointers, and gets filled in with the result of
// IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0862e03..4f9af16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -35,14 +35,17 @@
#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
+#include <ui/EdgeExtensionEffect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
#include <ui/StaticDisplayInfo.h>
+#include <android/gui/BnJankListener.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
@@ -337,6 +340,8 @@
static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>
getDisplayDecorationSupport(const sp<IBinder>& displayToken);
+ static bool flagEdgeExtensionEffectUseShader();
+
// ------------------------------------------------------------------------
// surface creation / destruction
@@ -447,7 +452,6 @@
uint64_t mId;
- uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
bool mEarlyWakeupStart = false;
bool mEarlyWakeupEnd = false;
@@ -743,11 +747,26 @@
Transaction& setStretchEffect(const sp<SurfaceControl>& sc,
const StretchEffect& stretchEffect);
+ /**
+ * Provides the edge extension effect configured on a container that the
+ * surface is rendered within.
+ * @param sc target surface the edge extension should be applied to
+ * @param effect the corresponding EdgeExtensionParameters to be applied
+ * to the surface.
+ * @return The transaction being constructed
+ */
+ Transaction& setEdgeExtensionEffect(const sp<SurfaceControl>& sc,
+ const gui::EdgeExtensionParameters& effect);
+
Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
const Rect& destinationFrame);
Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
+ Transaction& setBufferReleaseChannel(
+ const sp<SurfaceControl>& sc,
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -864,12 +883,82 @@
// ---------------------------------------------------------------------------
-class JankDataListener : public VirtualLightRefBase {
+class JankDataListener;
+
+// Acts as a representative listener to the composer for a single layer and
+// forwards any received jank data to multiple listeners. Will remove itself
+// from the composer only once the last listener is removed.
+class JankDataListenerFanOut : public gui::BnJankListener {
public:
- virtual ~JankDataListener() = 0;
- virtual void onJankDataAvailable(const std::vector<JankData>& jankData) = 0;
+ JankDataListenerFanOut(int32_t layerId) : mLayerId(layerId) {}
+
+ binder::Status onJankData(const std::vector<gui::JankData>& jankData) override;
+
+ static status_t addListener(sp<SurfaceControl> sc, sp<JankDataListener> listener);
+ static status_t removeListener(sp<JankDataListener> listener);
+
+private:
+ std::vector<sp<JankDataListener>> getActiveListeners();
+ bool removeListeners(const std::vector<wp<JankDataListener>>& listeners);
+ int64_t updateAndGetRemovalVSync();
+
+ struct WpJDLHash {
+ std::size_t operator()(const wp<JankDataListener>& listener) const {
+ return std::hash<JankDataListener*>{}(listener.unsafe_get());
+ }
+ };
+
+ std::mutex mMutex;
+ std::unordered_set<wp<JankDataListener>, WpJDLHash> mListeners GUARDED_BY(mMutex);
+ int32_t mLayerId;
+ int64_t mRemoveAfter = -1;
+
+ static std::mutex sFanoutInstanceMutex;
+ static std::unordered_map<int32_t, sp<JankDataListenerFanOut>> sFanoutInstances;
};
+// Base class for client listeners interested in jank classification data from
+// the composer. Subclasses should override onJankDataAvailable and call the add
+// and removal methods to receive jank data.
+class JankDataListener : public virtual RefBase {
+public:
+ JankDataListener() {}
+ virtual ~JankDataListener();
+
+ virtual bool onJankDataAvailable(const std::vector<gui::JankData>& jankData) = 0;
+
+ status_t addListener(sp<SurfaceControl> sc) {
+ if (mLayerId != -1) {
+ removeListener(0);
+ mLayerId = -1;
+ }
+
+ int32_t layerId = sc->getLayerId();
+ status_t status =
+ JankDataListenerFanOut::addListener(std::move(sc),
+ sp<JankDataListener>::fromExisting(this));
+ if (status == OK) {
+ mLayerId = layerId;
+ }
+ return status;
+ }
+
+ status_t removeListener(int64_t afterVsync) {
+ mRemoveAfter = std::max(static_cast<int64_t>(0), afterVsync);
+ return JankDataListenerFanOut::removeListener(sp<JankDataListener>::fromExisting(this));
+ }
+
+ status_t flushJankData();
+
+ friend class JankDataListenerFanOut;
+
+private:
+ int32_t mLayerId = -1;
+ int64_t mRemoveAfter = -1;
+};
+
+// ---------------------------------------------------------------------------
+
class TransactionCompletedListener : public BnTransactionCompletedListener {
public:
TransactionCompletedListener();
@@ -904,7 +993,6 @@
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
GUARDED_BY(mMutex);
- std::multimap<int32_t, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
std::unordered_map<ReleaseCallbackId, ReleaseBufferCallback, ReleaseBufferCallbackIdHash>
mReleaseBufferCallbacks GUARDED_BY(mMutex);
@@ -927,14 +1015,10 @@
const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
surfaceControls,
CallbackId::Type callbackType);
- CallbackId addCallbackFunctionLocked(
- const TransactionCompletedCallback& callbackFunction,
- const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls,
- CallbackId::Type callbackType) REQUIRES(mMutex);
- void addSurfaceControlToCallbacks(SurfaceComposerClient::CallbackInfo& callbackInfo,
- const sp<SurfaceControl>& surfaceControl);
+ void addSurfaceControlToCallbacks(
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id);
void removeQueueStallListener(void *id);
@@ -943,18 +1027,6 @@
TrustedPresentationCallback tpc, int id, void* context);
void clearTrustedPresentationCallback(int id);
- /*
- * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
- * surface. Jank classifications arrive as part of the transaction callbacks about previous
- * frames submitted to this Surface.
- */
- void addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl);
-
- /**
- * Removes a jank listener previously added to addJankCallback.
- */
- void removeJankListener(const sp<JankDataListener>& listener);
-
void addSurfaceStatsListener(void* context, void* cookie, sp<SurfaceControl> surfaceControl,
SurfaceStatsCallback listener);
void removeSurfaceStatsListener(void* context, void* cookie);
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index b7aba2b..7ddac81 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -59,8 +59,9 @@
// of the full parceling to happen on its native side.
status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
- private:
+ std::string toString() const;
+private:
static String16 readMaybeEmptyString16(const Parcel* parcel);
};
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 87cef08..d3f2899 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -43,3 +43,75 @@
purpose: PURPOSE_BUGFIX
}
} # trace_frame_rate_override
+
+flag {
+ name: "wb_consumer_base_owns_bq"
+ namespace: "core_graphics"
+ description: "ConsumerBase-based classes now own their own bufferqueue"
+ bug: "340933754"
+ is_fixed_read_only: true
+} # wb_consumer_base_owns_bq
+
+flag {
+ name: "wb_platform_api_improvements"
+ namespace: "core_graphics"
+ description: "Simple improvements to Surface and ConsumerBase classes"
+ bug: "340933794"
+ is_fixed_read_only: true
+} # wb_platform_api_improvements
+
+flag {
+ name: "wb_stream_splitter"
+ namespace: "core_graphics"
+ description: "Removes IGBP/IGBCs from Camera3StreamSplitter as part of BufferQueue refactors"
+ bug: "340933206"
+ is_fixed_read_only: true
+} # wb_stream_splitter
+
+flag {
+ name: "edge_extension_shader"
+ namespace: "windowing_frontend"
+ description: "Enable edge extension via shader"
+ bug: "322036393"
+ is_fixed_read_only: true
+} # edge_extension_shader
+
+flag {
+ name: "buffer_release_channel"
+ namespace: "window_surfaces"
+ description: "Enable BufferReleaseChannel to optimize buffer releases"
+ bug: "294133380"
+ is_fixed_read_only: true
+} # buffer_release_channel
+
+flag {
+ name: "wb_ring_buffer"
+ namespace: "core_graphics"
+ description: "Remove slot dependency in the Ring Buffer Consumer."
+ bug: "342197847"
+ is_fixed_read_only: true
+} # wb_ring_buffer
+
+flag {
+ name: "wb_camera3_and_processors"
+ namespace: "core_graphics"
+ description: "Remove usage of IGBPs in the *Processor and Camera3*"
+ bug: "342199002"
+ is_fixed_read_only: true
+} # wb_camera3_and_processors
+
+flag {
+ name: "wb_libcameraservice"
+ namespace: "core_graphics"
+ description: "Remove usage of IGBPs in the libcameraservice."
+ bug: "342197849"
+ is_fixed_read_only: true
+} # wb_libcameraservice
+
+flag {
+ name: "bq_producer_throttles_only_async_mode"
+ namespace: "core_graphics"
+ description: "BufferQueueProducer only CPU throttle on queueBuffer() in async mode."
+ bug: "359252619"
+ is_fixed_read_only: true
+} # bq_producer_throttles_only_async_mode
diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs
index fead018..2351df0 100644
--- a/libs/gui/rust/aidl_types/src/lib.rs
+++ b/libs/gui/rust/aidl_types/src/lib.rs
@@ -42,10 +42,7 @@
}
stub_unstructured_parcelable!(BitTube);
-stub_unstructured_parcelable!(CaptureArgs);
-stub_unstructured_parcelable!(DisplayCaptureArgs);
stub_unstructured_parcelable!(DisplayInfo);
-stub_unstructured_parcelable!(LayerCaptureArgs);
stub_unstructured_parcelable!(LayerDebugInfo);
stub_unstructured_parcelable!(LayerMetadata);
stub_unstructured_parcelable!(ParcelableVsyncEventData);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index ea8acbb..f07747f 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -12,6 +12,34 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aidl_interface {
+ name: "libgui_test_server_aidl",
+ unstable: true,
+ srcs: ["testserver/aidl/**/*.aidl"],
+ local_include_dir: "testserver/aidl",
+ include_dirs: [
+ "frameworks/native/aidl/gui",
+ ],
+ backend: {
+ cpp: {
+ enabled: true,
+ additional_shared_libraries: [
+ "libgui",
+ "libui",
+ ],
+ },
+ java: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: false,
+ },
+ rust: {
+ enabled: false,
+ },
+ },
+}
+
cc_test {
name: "libgui_test",
test_suites: ["device-tests"],
@@ -25,34 +53,41 @@
"-Wthread-safety",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
],
srcs: [
- "LibGuiMain.cpp", // Custom gtest entrypoint
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
+ "BufferReleaseChannel_test.cpp",
"Choreographer_test.cpp",
"CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
- "EndToEndNativeInputTest.cpp",
- "FrameRateUtilsTest.cpp",
- "DisplayInfo_test.cpp",
"DisplayedContentSampling_test.cpp",
+ "DisplayInfo_test.cpp",
+ "EndToEndNativeInputTest.cpp",
"FillBuffer.cpp",
+ "FrameRateUtilsTest.cpp",
"GLTest.cpp",
"IGraphicBufferProducer_test.cpp",
+ "LibGuiMain.cpp", // Custom gtest entrypoint
"Malicious.cpp",
"MultiTextureConsumer_test.cpp",
"RegionSampling_test.cpp",
"StreamSplitter_test.cpp",
+ "Surface_test.cpp",
"SurfaceTextureClient_test.cpp",
"SurfaceTextureFBO_test.cpp",
+ "SurfaceTextureGL_test.cpp",
"SurfaceTextureGLThreadToGL_test.cpp",
"SurfaceTextureGLToGL_test.cpp",
- "SurfaceTextureGL_test.cpp",
"SurfaceTextureMultiContextGL_test.cpp",
- "Surface_test.cpp",
+ "TestServer_test.cpp",
+ "testserver/TestServer.cpp",
+ "testserver/TestServerClient.cpp",
+ "testserver/TestServerHost.cpp",
"TextureRenderer.cpp",
"VsyncEventData_test.cpp",
"WindowInfo_test.cpp",
@@ -63,10 +98,17 @@
"android.hardware.configstore-utils",
"libSurfaceFlingerProp",
"libGLESv1_CM",
+ "libgui_test_server_aidl-cpp",
"libinput",
"libnativedisplay",
],
+ // This needs to get copied over for the test since it's not part of the
+ // platform.
+ data_libs: [
+ "libgui_test_server_aidl-cpp",
+ ],
+
static_libs: [
"libgmock",
],
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 946ff05..53f4a36 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -20,7 +20,7 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/graphics/common/1.2/types.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
#include <gui/FrameTimestamps.h>
@@ -186,6 +186,10 @@
mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber);
}
+ void setApplyToken(sp<IBinder> applyToken) {
+ mBlastBufferQueueAdapter->setApplyToken(std::move(applyToken));
+ }
+
private:
sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
@@ -229,7 +233,8 @@
ISurfaceComposerClient::eFXSurfaceBufferState,
/*parent*/ mRootSurfaceControl->getHandle());
- mCaptureArgs.sourceCrop = Rect(ui::Size(mDisplayWidth, mDisplayHeight));
+ mCaptureArgs.captureArgs.sourceCrop =
+ gui::aidl_utils::toARect(mDisplayWidth, mDisplayHeight);
mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
@@ -510,6 +515,69 @@
adapter.waitForCallbacks();
}
+class WaitForCommittedCallback {
+public:
+ WaitForCommittedCallback() = default;
+ ~WaitForCommittedCallback() = default;
+
+ void wait() {
+ std::unique_lock lock(mMutex);
+ cv.wait(lock, [this] { return mCallbackReceived; });
+ }
+
+ void notify() {
+ std::unique_lock lock(mMutex);
+ mCallbackReceived = true;
+ cv.notify_one();
+ mCallbackReceivedTimeStamp = std::chrono::system_clock::now();
+ }
+ auto getCallback() {
+ return [this](void* /* unused context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) { notify(); };
+ }
+ std::chrono::time_point<std::chrono::system_clock> mCallbackReceivedTimeStamp;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable cv;
+ bool mCallbackReceived = false;
+};
+
+TEST_F(BLASTBufferQueueTest, setApplyToken) {
+ sp<IBinder> applyToken = sp<BBinder>::make();
+ WaitForCommittedCallback firstTransaction;
+ WaitForCommittedCallback secondTransaction;
+ {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ adapter.setApplyToken(applyToken);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction t;
+ t.addTransactionCommittedCallback(firstTransaction.getCallback(), nullptr);
+ adapter.mergeWithNextTransaction(&t, 1);
+ queueBuffer(igbProducer, 127, 127, 127,
+ /*presentTimeDelay*/ std::chrono::nanoseconds(500ms).count());
+ }
+ {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ adapter.setApplyToken(applyToken);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction t;
+ t.addTransactionCommittedCallback(secondTransaction.getCallback(), nullptr);
+ adapter.mergeWithNextTransaction(&t, 1);
+ queueBuffer(igbProducer, 127, 127, 127, /*presentTimeDelay*/ 0);
+ }
+
+ firstTransaction.wait();
+ secondTransaction.wait();
+ EXPECT_GT(secondTransaction.mCallbackReceivedTimeStamp,
+ firstTransaction.mCallbackReceivedTimeStamp);
+}
+
TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
uint8_t r = 255;
uint8_t g = 0;
@@ -1269,6 +1337,20 @@
}
};
+class TestSurfaceListener : public SurfaceListener {
+public:
+ sp<IGraphicBufferProducer> mIgbp;
+ TestSurfaceListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {}
+ void onBufferReleased() override {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ mIgbp->detachNextBuffer(&buffer, &fence);
+ }
+ bool needsReleaseNotify() override { return true; }
+ void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {}
+ void onBufferDetached(int /*slot*/) {}
+};
+
TEST_F(BLASTBufferQueueTest, CustomProducerListener) {
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer();
@@ -1327,7 +1409,7 @@
ASSERT_EQ(ui::Transform::ROT_0, static_cast<ui::Transform::RotationFlags>(transformHint));
ASSERT_EQ(NO_ERROR,
- surface->connect(NATIVE_WINDOW_API_CPU, new TestProducerListener(igbProducer)));
+ surface->connect(NATIVE_WINDOW_API_CPU, new TestSurfaceListener(igbProducer)));
// After connecting to the surface, we should get the correct hint.
surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 6564534..3b6a66e 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -17,10 +17,12 @@
#define LOG_TAG "BufferItemConsumer_test"
//#define LOG_NDEBUG 0
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <ui/GraphicBuffer.h>
namespace android {
@@ -43,15 +45,26 @@
BufferItemConsumerTest* mTest;
};
+ struct TrackingProducerListener : public BnProducerListener {
+ TrackingProducerListener(BufferItemConsumerTest* test) : mTest(test) {}
+
+ virtual void onBufferReleased() override {}
+ virtual bool needsReleaseNotify() override { return true; }
+ virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {}
+ virtual void onBufferDetached(int slot) override { mTest->HandleBufferDetached(slot); }
+
+ BufferItemConsumerTest* mTest;
+ };
+
void SetUp() override {
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mBIC = new BufferItemConsumer(mConsumer, kUsage, kMaxLockedBuffers, true);
+ mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
mBFL = new BufferFreedListener(this);
mBIC->setBufferFreedListener(mBFL);
- sp<IProducerListener> producerListener = new StubProducerListener();
+ sp<IProducerListener> producerListener = new TrackingProducerListener(this);
+ mProducer = mBIC->getSurface()->getIGraphicBufferProducer();
IGraphicBufferProducer::QueueBufferOutput bufferOutput;
ASSERT_EQ(NO_ERROR,
mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -71,6 +84,13 @@
ALOGD("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
}
+ void HandleBufferDetached(int slot) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mDetachedBufferSlots.push_back(slot);
+ ALOGD("HandleBufferDetached, slot=%d mDetachedBufferSlots-count=%zu", slot,
+ mDetachedBufferSlots.size());
+ }
+
void DequeueBuffer(int* outSlot) {
ASSERT_NE(outSlot, nullptr);
@@ -120,6 +140,7 @@
std::mutex mMutex;
int mFreedBufferCount{0};
+ std::vector<int> mDetachedBufferSlots = {};
sp<BufferItemConsumer> mBIC;
sp<BufferFreedListener> mBFL;
@@ -203,4 +224,19 @@
ASSERT_EQ(1, GetFreedBufferCount());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+// Test that delete BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+
+ sp<GraphicBuffer> buffer = mBuffers[slot];
+ EXPECT_EQ(OK, mBIC->detachBuffer(buffer));
+ EXPECT_THAT(mDetachedBufferSlots, testing::ElementsAre(slot));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 590e2c8..2e6ffcb 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1430,19 +1430,15 @@
}
struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
- BufferItemConsumerSetFrameRateListener(const sp<IGraphicBufferConsumer>& consumer)
- : BufferItemConsumer(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
+ BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
MOCK_METHOD(void, onSetFrameRate, (float, int8_t, int8_t), (override));
};
TEST_F(BufferQueueTest, TestSetFrameRate) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumerSetFrameRateListener> bufferConsumer =
- sp<BufferItemConsumerSetFrameRateListener>::make(consumer);
+ sp<BufferItemConsumerSetFrameRateListener>::make();
+ sp<IGraphicBufferProducer> producer = bufferConsumer->getSurface()->getIGraphicBufferProducer();
EXPECT_CALL(*bufferConsumer, onSetFrameRate(12.34f, 1, 0)).Times(1);
producer->setFrameRate(12.34f, 1, 0);
@@ -1493,14 +1489,10 @@
// See b/270004534
TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumer> bufferConsumer =
- sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2);
ASSERT_NE(nullptr, bufferConsumer.get());
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<Surface> surface = bufferConsumer->getSurface();
native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
native_window_set_buffers_dimensions(surface.get(), 100, 100);
@@ -1531,14 +1523,10 @@
}
TEST_F(BufferQueueTest, TestAdditionalOptions) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumer> bufferConsumer =
- sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2);
ASSERT_NE(nullptr, bufferConsumer.get());
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<Surface> surface = bufferConsumer->getSurface();
native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
native_window_set_buffers_dimensions(surface.get(), 100, 100);
diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp
new file mode 100644
index 0000000..11d122b
--- /dev/null
+++ b/libs/gui/tests/BufferReleaseChannel_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <gui/BufferReleaseChannel.h>
+
+using namespace std::string_literals;
+using android::gui::BufferReleaseChannel;
+
+namespace android {
+
+namespace {
+
+// Helper function to check if two file descriptors point to the same file.
+bool is_same_file(int fd1, int fd2) {
+ struct stat stat1;
+ if (fstat(fd1, &stat1) != 0) {
+ return false;
+ }
+ struct stat stat2;
+ if (fstat(fd2, &stat2) != 0) {
+ return false;
+ }
+ return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
+}
+
+} // namespace
+
+TEST(BufferReleaseChannelTest, MessageFlattenable) {
+ ReleaseCallbackId releaseCallbackId{1, 2};
+ sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
+ uint32_t maxAcquiredBufferCount = 5;
+
+ std::vector<uint8_t> dataBuffer;
+ std::vector<int> fdBuffer;
+
+ // Verify that we can flatten a message
+ {
+ BufferReleaseChannel::Message message{releaseCallbackId, releaseFence,
+ maxAcquiredBufferCount};
+
+ dataBuffer.resize(message.getFlattenedSize());
+ void* dataPtr = dataBuffer.data();
+ size_t dataSize = dataBuffer.size();
+
+ fdBuffer.resize(message.getFdCount());
+ int* fdPtr = fdBuffer.data();
+ size_t fdSize = fdBuffer.size();
+
+ ASSERT_EQ(OK, message.flatten(dataPtr, dataSize, fdPtr, fdSize));
+
+ // Fence's unique_fd uses fdsan to check ownership of the file descriptor. Normally the file
+ // descriptor is passed through the Unix socket and duplicated (and sent to another process)
+ // so there's no problem with duplicate file descriptor ownership. For this unit test, we
+ // need to set up a duplicate file descriptor to avoid crashing due to duplicate ownership.
+ ASSERT_EQ(releaseFence->get(), fdBuffer[0]);
+ fdBuffer[0] = message.releaseFence->dup();
+ }
+
+ // Verify that we can unflatten a message
+ {
+ BufferReleaseChannel::Message message;
+
+ const void* dataPtr = dataBuffer.data();
+ size_t dataSize = dataBuffer.size();
+
+ const int* fdPtr = fdBuffer.data();
+ size_t fdSize = fdBuffer.size();
+
+ ASSERT_EQ(OK, message.unflatten(dataPtr, dataSize, fdPtr, fdSize));
+ ASSERT_EQ(releaseCallbackId, message.releaseCallbackId);
+ ASSERT_TRUE(is_same_file(releaseFence->get(), message.releaseFence->get()));
+ ASSERT_EQ(maxAcquiredBufferCount, message.maxAcquiredBufferCount);
+ }
+}
+
+// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message
+// available.
+TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
+ ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
+
+ ReleaseCallbackId releaseCallbackId;
+ sp<Fence> releaseFence;
+ uint32_t maxAcquiredBufferCount;
+ ASSERT_EQ(WOULD_BLOCK,
+ consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
+}
+
+// Verify that we can write a message to the BufferReleaseChannel producer and read that message
+// using the BufferReleaseChannel consumer.
+TEST(BufferReleaseChannelTest, ProduceAndConsume) {
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
+ ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
+
+ sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
+
+ for (uint64_t i = 0; i < 64; i++) {
+ ReleaseCallbackId producerId{i, i + 1};
+ uint32_t maxAcquiredBufferCount = i + 2;
+ ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
+ }
+
+ for (uint64_t i = 0; i < 64; i++) {
+ ReleaseCallbackId expectedId{i, i + 1};
+ uint32_t expectedMaxAcquiredBufferCount = i + 2;
+
+ ReleaseCallbackId consumerId;
+ sp<Fence> consumerFence;
+ uint32_t maxAcquiredBufferCount;
+ ASSERT_EQ(OK,
+ consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
+
+ ASSERT_EQ(expectedId, consumerId);
+ ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get()));
+ ASSERT_EQ(expectedMaxAcquiredBufferCount, maxAcquiredBufferCount);
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
index 2ac2550..8db48d2 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -52,25 +52,23 @@
sp<Looper> looper = Looper::prepare(0);
Choreographer* choreographer = Choreographer::getForThread();
VsyncCallback animationCb;
- VsyncCallback inputCb;
-
choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
CALLBACK_ANIMATION);
+ VsyncCallback inputCb;
choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0,
CALLBACK_INPUT);
-
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t currTime;
- int pollResult;
+ auto startTime = std::chrono::system_clock::now();
do {
- pollResult = looper->pollOnce(16);
- currTime = systemTime(SYSTEM_TIME_MONOTONIC);
- } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) &&
- (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) &&
- (currTime - startTime < 3000));
-
- ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback";
- ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback";
+ static constexpr int32_t timeoutMs = 1000;
+ int pollResult = looper->pollOnce(timeoutMs);
+ ASSERT_TRUE((pollResult != Looper::POLL_TIMEOUT) && (pollResult != Looper::POLL_ERROR))
+ << "Failed to poll looper. Poll result = " << pollResult;
+ auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now() - startTime);
+ ASSERT_LE(elapsedMs.count(), timeoutMs)
+ << "Timed out waiting for callbacks. inputCb=" << inputCb.callbackReceived()
+ << " animationCb=" << animationCb.callbackReceived();
+ } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()));
ASSERT_EQ(inputCb.frameTime, animationCb.frameTime)
<< android::base::StringPrintf("input and animation callback frame times don't match. "
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index d80bd9c..f4239cb 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,13 +66,10 @@
test_info->name(),
params.width, params.height,
params.maxLockedBuffers, params.format);
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mCC = new CpuConsumer(consumer, params.maxLockedBuffers);
+ mCC = new CpuConsumer(params.maxLockedBuffers);
String8 name("CpuConsumer_Under_Test");
mCC->setName(name);
- mSTC = new Surface(producer);
+ mSTC = mCC->getSurface();
mANW = mSTC;
}
diff --git a/libs/gui/tests/LibGuiMain.cpp b/libs/gui/tests/LibGuiMain.cpp
index 10f7207..7c7c2cc 100644
--- a/libs/gui/tests/LibGuiMain.cpp
+++ b/libs/gui/tests/LibGuiMain.cpp
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-#include "gtest/gtest.h"
-#include "log/log.h"
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "testserver/TestServer.h"
+#include "testserver/TestServerClient.h"
+#include "testserver/TestServerHost.h"
+
+using namespace android;
namespace {
@@ -32,7 +39,34 @@
} // namespace
int main(int argc, char** argv) {
+ // There are three modes that we can run in to support the libgui TestServer:
+ //
+ // - libgui_test : normal mode, runs tests and fork/execs the testserver host process
+ // - libgui_test --test-server-host $recvPipeFd $sendPipeFd : TestServerHost mode, listens on
+ // $recvPipeFd for commands and sends responses over $sendPipeFd
+ // - libgui_test --test-server $name : TestServer mode, starts a ITestService binder service
+ // under $name
+ for (int i = 1; i < argc; i++) {
+ std::string arg = argv[i];
+ if (arg == "--test-server-host") {
+ LOG_ALWAYS_FATAL_IF(argc < (i + 2), "--test-server-host requires two pipe fds");
+ // Note that the send/recv are from our perspective.
+ base::unique_fd recvPipeFd = base::unique_fd(atoi(argv[i + 1]));
+ base::unique_fd sendPipeFd = base::unique_fd(atoi(argv[i + 2]));
+ return TestServerHostMain(argv[0], std::move(sendPipeFd), std::move(recvPipeFd));
+ }
+ if (arg == "--test-server") {
+ LOG_ALWAYS_FATAL_IF(argc < (i + 1), "--test-server requires a name");
+ return TestServerMain(argv[i + 1]);
+ }
+ }
testing::InitGoogleTest(&argc, argv);
testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger());
+
+ // This has to be run *before* any test initialization, because it fork/execs a TestServerHost,
+ // which will later create new binder service. You can't do that in a forked thread after you've
+ // initialized any binder stuff, which some tests do.
+ TestServerClient::InitializeOrDie(argv[0]);
+
return RUN_ALL_TESTS();
}
\ No newline at end of file
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 7d3d4aa..2428bb3 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,12 +34,8 @@
virtual void SetUp() {
GLTest::SetUp();
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mGlConsumer = new GLConsumer(consumer, TEX_ID,
- GLConsumer::TEXTURE_EXTERNAL, true, false);
- mSurface = new Surface(producer);
+ mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSurface = mGlConsumer->getSurface();
mANW = mSurface.get();
}
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 223e4b6..a0d8c53 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -19,7 +19,7 @@
#include <android/gui/BnRegionSamplingListener.h>
#include <binder/ProcessState.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b28dca8..59d05b6 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,12 +40,8 @@
}
virtual void SetUp() {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mST = new GLConsumer(consumer, 123, GLConsumer::TEXTURE_EXTERNAL, true,
- false);
- mSTC = new Surface(producer);
+ mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSTC = mST->getSurface();
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
@@ -731,12 +727,8 @@
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<GLConsumer> st(new GLConsumer(consumer, i,
- GLConsumer::TEXTURE_EXTERNAL, true, false));
- sp<Surface> stc(new Surface(producer));
+ sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false));
+ sp<Surface> stc = st->getSurface();
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 9d8af5d..1309635 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,11 +38,8 @@
void SetUp() {
GLTest::SetUp();
- sp<IGraphicBufferProducer> producer;
- BufferQueue::createBufferQueue(&producer, &mConsumer);
- mST = new GLConsumer(mConsumer, TEX_ID, GLConsumer::TEXTURE_EXTERNAL,
- true, false);
- mSTC = new Surface(producer);
+ mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSTC = mST->getSurface();
mANW = mSTC;
ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
@@ -63,7 +60,6 @@
mTextureRenderer->drawTexture();
}
- sp<IGraphicBufferConsumer> mConsumer;
sp<GLConsumer> mST;
sp<Surface> mSTC;
sp<ANativeWindow> mANW;
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index f76c0be..449533a 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -480,8 +480,8 @@
};
sp<DisconnectWaiter> dw(new DisconnectWaiter());
- mConsumer->consumerConnect(dw, false);
-
+ sp<IGraphicBufferConsumer> consumer = mST->getIGraphicBufferConsumer();
+ consumer->consumerConnect(dw, false);
sp<Thread> pt(new ProducerThread(mANW));
pt->run("ProducerThread");
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 43cd0f8..88893b6 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "gui/view/Surface.h"
#include "Constants.h"
#include "MockConsumer.h"
@@ -23,28 +24,41 @@
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/ISurfaceComposer.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware_buffer.h>
#include <binder/ProcessState.h>
+#include <com_android_graphics_libgui_flags.h>
#include <configstore/Utils.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItemConsumer.h>
-#include <gui/IProducerListener.h>
+#include <gui/BufferQueue.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
-#include <inttypes.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <sys/types.h>
+#include <system/window.h>
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
+#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <utils/Errors.h>
#include <utils/String8.h>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <future>
#include <limits>
#include <thread>
+#include "testserver/TestServerClient.h"
+
namespace android {
using namespace std::chrono_literals;
@@ -82,7 +96,7 @@
virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) {
mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end());
}
-
+ virtual void onBufferDetached(int /*slot*/) {}
int getReleaseNotifyCount() const {
return mBuffersReleased;
}
@@ -97,6 +111,18 @@
std::vector<sp<GraphicBuffer>> mDiscardedBuffers;
};
+class DeathWatcherListener : public StubSurfaceListener {
+public:
+ virtual void onRemoteDied() { mDiedPromise.set_value(true); }
+
+ virtual bool needsDeathNotify() { return true; }
+
+ std::future<bool> getDiedFuture() { return mDiedPromise.get_future(); }
+
+private:
+ std::promise<bool> mDiedPromise;
+};
+
class SurfaceTest : public ::testing::Test {
protected:
SurfaceTest() {
@@ -143,10 +169,10 @@
if (hasSurfaceListener) {
listener = new FakeSurfaceListener(enableReleasedCb);
}
- ASSERT_EQ(OK, surface->connect(
- NATIVE_WINDOW_API_CPU,
- /*reportBufferRemoval*/true,
- /*listener*/listener));
+ ASSERT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU,
+ /*listener*/ listener,
+ /*reportBufferRemoval*/ true));
const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
@@ -264,13 +290,9 @@
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<BufferItemConsumer> c = new BufferItemConsumer(consumer,
- TEST_USAGE_FLAGS);
- sp<Surface> s = new Surface(producer);
+ sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS);
+ sp<Surface> s = c->getSurface();
sp<ANativeWindow> anw(s);
int flags = -1;
@@ -282,15 +304,11 @@
TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE);
- sp<Surface> s = new Surface(producer);
-
+ sp<Surface> s = cpuConsumer->getSurface();
sp<ANativeWindow> anw(s);
android_dataspace dataSpace;
@@ -303,11 +321,8 @@
}
TEST_F(SurfaceTest, SettingGenerationNumber) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
// Allocate a buffer with a generation number of 0
@@ -491,11 +506,11 @@
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- sp<StubProducerListener> listener = new StubProducerListener();
- ASSERT_EQ(OK, surface->connect(
- NATIVE_WINDOW_API_CPU,
- /*listener*/listener,
- /*reportBufferRemoval*/true));
+ sp<StubSurfaceListener> listener = new StubSurfaceListener();
+ ASSERT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU,
+ /*listener*/ listener,
+ /*reportBufferRemoval*/ true));
const int BUFFER_COUNT = 4;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
@@ -636,7 +651,7 @@
status_t setTransactionState(
const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
@@ -987,6 +1002,19 @@
binder::Status notifyShutdown() override { return binder::Status::ok(); }
+ binder::Status addJankListener(const sp<IBinder>& /*layer*/,
+ const sp<gui::IJankListener>& /*listener*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status flushJankData(int32_t /*layerId*/) override { return binder::Status::ok(); }
+
+ binder::Status removeJankListener(int32_t /*layerId*/,
+ const sp<gui::IJankListener>& /*listener*/,
+ int64_t /*afterVsync*/) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -2134,14 +2162,11 @@
TEST_F(SurfaceTest, BatchOperations) {
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
- sp<StubProducerListener> listener = new StubProducerListener();
+ sp<StubSurfaceListener> listener = new StubSurfaceListener();
ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
/*reportBufferRemoval*/false));
@@ -2186,14 +2211,11 @@
TEST_F(SurfaceTest, BatchIllegalOperations) {
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
- sp<StubProducerListener> listener = new StubProducerListener();
+ sp<StubSurfaceListener> listener = new StubSurfaceListener();
ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
/*reportBufferRemoval*/false));
@@ -2213,4 +2235,288 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+TEST_F(SurfaceTest, PlatformBufferMethods) {
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+
+ EXPECT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false));
+
+ //
+ // Verify nullptrs are handled safely:
+ //
+
+ EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp<GraphicBuffer>*)nullptr, nullptr));
+ EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp<GraphicBuffer>*)nullptr, &fence));
+ EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer(&buffer, nullptr));
+ EXPECT_EQ(BAD_VALUE, surface->queueBuffer(nullptr, nullptr));
+ EXPECT_EQ(BAD_VALUE, surface->detachBuffer(nullptr));
+
+ //
+ // Verify dequeue/queue:
+ //
+
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_NE(nullptr, buffer);
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence));
+
+ //
+ // Verify dequeue/detach:
+ //
+
+ wp<GraphicBuffer> weakBuffer;
+ {
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+
+ EXPECT_EQ(OK, surface->detachBuffer(buffer));
+
+ weakBuffer = buffer;
+ buffer = nullptr;
+ }
+ EXPECT_EQ(nullptr, weakBuffer.promote()) << "Weak buffer still held by Surface.";
+
+ //
+ // Verify detach without borrowing the buffer does not work:
+ //
+
+ sp<GraphicBuffer> heldTooLongBuffer;
+ EXPECT_EQ(OK, surface->dequeueBuffer(&heldTooLongBuffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(heldTooLongBuffer));
+ EXPECT_EQ(BAD_VALUE, surface->detachBuffer(heldTooLongBuffer));
+}
+
+TEST_F(SurfaceTest, AllowAllocation) {
+ // controlledByApp must be true to disable blocking
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1, /*controlledByApp*/ true);
+ sp<Surface> surface = cpuConsumer->getSurface();
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+
+ EXPECT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false));
+ EXPECT_EQ(OK, surface->allowAllocation(false));
+
+ EXPECT_EQ(OK, surface->setDequeueTimeout(-1));
+ EXPECT_EQ(WOULD_BLOCK, surface->dequeueBuffer(&buffer, &fence));
+
+ EXPECT_EQ(OK, surface->setDequeueTimeout(10));
+ EXPECT_EQ(TIMED_OUT, surface->dequeueBuffer(&buffer, &fence));
+
+ EXPECT_EQ(OK, surface->allowAllocation(true));
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+}
+
+TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) {
+ class DequeuingSurfaceListener : public SurfaceListener {
+ public:
+ DequeuingSurfaceListener(const wp<Surface>& surface) : mSurface(surface) {}
+
+ virtual void onBufferReleased() override {
+ sp<Surface> surface = mSurface.promote();
+ ASSERT_NE(nullptr, surface);
+ EXPECT_EQ(OK, surface->dequeueBuffer(&mBuffer, &mFence));
+ }
+
+ virtual bool needsReleaseNotify() override { return true; }
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>&) override {}
+ virtual void onBufferDetached(int) override {}
+
+ sp<GraphicBuffer> mBuffer;
+ sp<Fence> mFence;
+
+ private:
+ wp<Surface> mSurface;
+ };
+
+ class ImmediateReleaseConsumerListener : public BufferItemConsumer::FrameAvailableListener {
+ public:
+ ImmediateReleaseConsumerListener(const wp<BufferItemConsumer>& consumer)
+ : mConsumer(consumer) {}
+
+ virtual void onFrameAvailable(const BufferItem&) override {
+ sp<BufferItemConsumer> consumer = mConsumer.promote();
+ ASSERT_NE(nullptr, consumer);
+
+ mCalls += 1;
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, consumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK, consumer->releaseBuffer(buffer));
+ }
+
+ size_t mCalls = 0;
+
+ private:
+ wp<BufferItemConsumer> mConsumer;
+ };
+
+ sp<IGraphicBufferProducer> bqProducer;
+ sp<IGraphicBufferConsumer> bqConsumer;
+ BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
+
+ sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3);
+ sp<Surface> surface = sp<Surface>::make(bqProducer);
+ sp<ImmediateReleaseConsumerListener> consumerListener =
+ sp<ImmediateReleaseConsumerListener>::make(consumer);
+ consumer->setFrameAvailableListener(consumerListener);
+
+ sp<DequeuingSurfaceListener> surfaceListener = sp<DequeuingSurfaceListener>::make(surface);
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener, false));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(2));
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence));
+
+ EXPECT_EQ(1u, consumerListener->mCalls);
+ EXPECT_NE(nullptr, surfaceListener->mBuffer);
+
+ EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
+TEST_F(SurfaceTest, ViewSurface_toString) {
+ view::Surface surface{};
+ EXPECT_EQ("", surface.toString());
+
+ surface.name = String16("name");
+ EXPECT_EQ("name", surface.toString());
+}
+
+TEST_F(SurfaceTest, TestRemoteSurfaceDied_CallbackCalled) {
+ sp<TestServerClient> testServer = TestServerClient::Create();
+ sp<IGraphicBufferProducer> producer = testServer->CreateProducer();
+ EXPECT_NE(nullptr, producer);
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<DeathWatcherListener> deathWatcher = sp<DeathWatcherListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, deathWatcher));
+
+ auto diedFuture = deathWatcher->getDiedFuture();
+ EXPECT_EQ(OK, testServer->Kill());
+
+ diedFuture.wait();
+ EXPECT_TRUE(diedFuture.get());
+}
+
+TEST_F(SurfaceTest, TestRemoteSurfaceDied_Disconnect_CallbackNotCalled) {
+ sp<TestServerClient> testServer = TestServerClient::Create();
+ sp<IGraphicBufferProducer> producer = testServer->CreateProducer();
+ EXPECT_NE(nullptr, producer);
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<DeathWatcherListener> deathWatcher = sp<DeathWatcherListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, deathWatcher));
+ EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU));
+
+ auto watcherDiedFuture = deathWatcher->getDiedFuture();
+ EXPECT_EQ(OK, testServer->Kill());
+
+ std::future_status status = watcherDiedFuture.wait_for(std::chrono::seconds(1));
+ EXPECT_EQ(std::future_status::timeout, status);
+}
+
+TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements) {
+ sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN);
+ ASSERT_EQ(OK, consumer->setMaxBufferCount(3));
+ ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1));
+
+ sp<Surface> surface = consumer->getSurface();
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+
+ // Async mode sets up an extra buffer so the surface can queue it without waiting.
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(1));
+ ASSERT_EQ(OK, surface->setAsyncMode(true));
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ SurfaceQueueBufferOutput output;
+ BufferItem item;
+
+ // We can queue directly, without an output arg.
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence));
+ EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0));
+ EXPECT_EQ(OK, consumer->releaseBuffer(item));
+
+ // We can queue with an output arg, and that we don't expect to see a replacement.
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output));
+ EXPECT_FALSE(output.bufferReplaced);
+
+ // We expect see a replacement when we queue a second buffer in async mode, and the consumer
+ // hasn't acquired the first one yet.
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output));
+ EXPECT_TRUE(output.bufferReplaced);
+}
+
+TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements_Plural) {
+ sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN);
+ ASSERT_EQ(OK, consumer->setMaxBufferCount(4));
+ ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1));
+
+ sp<Surface> surface = consumer->getSurface();
+ consumer->setName(String8("TRPTest"));
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+
+ // Async mode sets up an extra buffer so the surface can queue it without waiting.
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(2));
+ ASSERT_EQ(OK, surface->setAsyncMode(true));
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+ // dequeueBuffers requires a vector of a certain size:
+ std::vector<Surface::BatchBuffer> buffers(2);
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers;
+ std::vector<SurfaceQueueBufferOutput> outputs;
+ BufferItem item;
+
+ auto moveBuffersToQueuedBuffers = [&]() {
+ EXPECT_EQ(2u, buffers.size());
+ EXPECT_NE(nullptr, buffers[0].buffer);
+ EXPECT_NE(nullptr, buffers[1].buffer);
+
+ queuedBuffers.clear();
+ for (auto& buffer : buffers) {
+ auto& queuedBuffer = queuedBuffers.emplace_back();
+ queuedBuffer.buffer = buffer.buffer;
+ queuedBuffer.fenceFd = buffer.fenceFd;
+ queuedBuffer.timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ }
+ buffers = {{}, {}};
+ };
+
+ // We can queue directly, without an output arg.
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ moveBuffersToQueuedBuffers();
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers));
+ EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0));
+ EXPECT_EQ(OK, consumer->releaseBuffer(item));
+
+ // We can queue with an output arg. Only the second one should be replaced.
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ moveBuffersToQueuedBuffers();
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+ EXPECT_EQ(2u, outputs.size());
+ EXPECT_FALSE(outputs[0].bufferReplaced);
+ EXPECT_TRUE(outputs[1].bufferReplaced);
+
+ // Since we haven't acquired anything, both queued buffers will replace the original one.
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ moveBuffersToQueuedBuffers();
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+ EXPECT_EQ(2u, outputs.size());
+ EXPECT_TRUE(outputs[0].bufferReplaced);
+ EXPECT_TRUE(outputs[1].bufferReplaced);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
} // namespace android
diff --git a/libs/gui/tests/TestServer_test.cpp b/libs/gui/tests/TestServer_test.cpp
new file mode 100644
index 0000000..d640782
--- /dev/null
+++ b/libs/gui/tests/TestServer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware_buffer.h>
+#include <binder/ProcessState.h>
+#include <com_android_graphics_libgui_flags.h>
+#include <configstore/Utils.h>
+#include <gui/AidlUtil.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+#include <sys/types.h>
+#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
+#include <ui/DisplayMode.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <cstddef>
+#include <limits>
+#include <thread>
+
+#include "binder/IInterface.h"
+#include "testserver/TestServerClient.h"
+
+namespace android {
+
+namespace {
+
+class TestServerTest : public ::testing::Test {
+protected:
+ TestServerTest() { ProcessState::self()->startThreadPool(); }
+};
+
+} // namespace
+
+TEST_F(TestServerTest, Create) {
+ EXPECT_NE(nullptr, TestServerClient::Create());
+}
+
+TEST_F(TestServerTest, CreateProducer) {
+ sp<TestServerClient> client = TestServerClient::Create();
+ EXPECT_NE(nullptr, client->CreateProducer());
+}
+
+TEST_F(TestServerTest, KillServer) {
+ class DeathWaiter : public IBinder::DeathRecipient {
+ public:
+ virtual void binderDied(const wp<IBinder>&) override { mPromise.set_value(true); }
+ std::future<bool> getFuture() { return mPromise.get_future(); }
+
+ std::promise<bool> mPromise;
+ };
+
+ sp<TestServerClient> client = TestServerClient::Create();
+ sp<IGraphicBufferProducer> producer = client->CreateProducer();
+ EXPECT_NE(nullptr, producer);
+
+ sp<DeathWaiter> deathWaiter = sp<DeathWaiter>::make();
+ EXPECT_EQ(OK, IInterface::asBinder(producer)->linkToDeath(deathWaiter));
+
+ auto deathWaiterFuture = deathWaiter->getFuture();
+ EXPECT_EQ(OK, client->Kill());
+ EXPECT_EQ(nullptr, client->CreateProducer());
+
+ EXPECT_TRUE(deathWaiterFuture.get());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServer.cpp b/libs/gui/tests/testserver/TestServer.cpp
new file mode 100644
index 0000000..cd8824e
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServer.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TestServer"
+
+#include <android-base/stringprintf.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/view/Surface.h>
+#include <libgui_test_server/BnTestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "TestServer.h"
+
+namespace android {
+
+namespace {
+class TestConsumerListener : public BnConsumerListener {
+ virtual void onFrameAvailable(const BufferItem&) override {}
+ virtual void onBuffersReleased() override {}
+ virtual void onSidebandStreamChanged() override {}
+};
+
+class TestServiceImpl : public libgui_test_server::BnTestServer {
+public:
+ TestServiceImpl(const char* name) : mName(name) {}
+
+ virtual binder::Status createProducer(view::Surface* out) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ BufferQueueHolder bq;
+ BufferQueue::createBufferQueue(&bq.producer, &bq.consumer);
+ sp<TestConsumerListener> listener = sp<TestConsumerListener>::make();
+ bq.consumer->consumerConnect(listener, /*controlledByApp*/ true);
+
+ uint64_t id = 0;
+ bq.producer->getUniqueId(&id);
+ std::string name = base::StringPrintf("%s-%" PRIu64, mName, id);
+
+ out->name = String16(name.c_str());
+ out->graphicBufferProducer = bq.producer;
+ mBqs.push_back(std::move(bq));
+
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status killNow() override {
+ ALOGE("LibGUI Test Service %s dying in response to killNow", mName);
+ _exit(0);
+ // Not reached:
+ return binder::Status::ok();
+ }
+
+private:
+ std::mutex mMutex;
+ const char* mName;
+
+ struct BufferQueueHolder {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ };
+
+ std::vector<BufferQueueHolder> mBqs;
+};
+} // namespace
+
+int TestServerMain(const char* name) {
+ ProcessState::self()->startThreadPool();
+
+ sp<TestServiceImpl> testService = sp<TestServiceImpl>::make(name);
+ ALOGE("service");
+ sp<IServiceManager> serviceManager(defaultServiceManager());
+ LOG_ALWAYS_FATAL_IF(OK != serviceManager->addService(String16(name), testService));
+
+ ALOGD("LibGUI Test Service %s STARTED", name);
+
+ IPCThreadState::self()->joinThreadPool();
+
+ ALOGW("LibGUI Test Service %s DIED", name);
+
+ return 0;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServer.h b/libs/gui/tests/testserver/TestServer.h
new file mode 100644
index 0000000..4226f1b
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServer.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+namespace android {
+
+/*
+ * Main method for a libgui ITestServer server.
+ *
+ * This must be called without any binder setup having been done, because you can't fork and do
+ * binder things once ProcessState is set up.
+ * @param name The service name of the test server to start.
+ * @return retcode
+ */
+int TestServerMain(const char* name);
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerClient.cpp b/libs/gui/tests/testserver/TestServerClient.cpp
new file mode 100644
index 0000000..e388074
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerClient.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/wait.h>
+#include <cerrno>
+#define LOG_TAG "TestServerClient"
+
+#include <android-base/stringprintf.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <libgui_test_server/ITestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <csignal>
+#include <cstdlib>
+#include <mutex>
+#include <string>
+
+#include "TestServerClient.h"
+#include "TestServerCommon.h"
+
+namespace android {
+
+namespace {
+
+std::string GetUniqueServiceName() {
+ static std::atomic<int> uniqueId = 1;
+
+ pid_t pid = getpid();
+ int id = uniqueId++;
+ return base::StringPrintf("Libgui-TestServer-%d-%d", pid, id);
+}
+
+struct RemoteTestServerHostHolder {
+ RemoteTestServerHostHolder(pid_t pid, int sendFd, int recvFd)
+ : mPid(pid), mSendFd(sendFd), mRecvFd(recvFd) {}
+ ~RemoteTestServerHostHolder() {
+ std::lock_guard lock(mMutex);
+
+ kill(mPid, SIGKILL);
+ close(mSendFd);
+ close(mRecvFd);
+ }
+
+ pid_t CreateTestServerOrDie(std::string name) {
+ std::lock_guard lock(mMutex);
+
+ CreateServerRequest request;
+ strlcpy(request.name, name.c_str(), sizeof(request.name) / sizeof(request.name[0]));
+
+ ssize_t bytes = write(mSendFd, &request, sizeof(request));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(request));
+
+ CreateServerResponse response;
+ bytes = read(mRecvFd, &response, sizeof(response));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(response));
+
+ return response.pid;
+ }
+
+private:
+ std::mutex mMutex;
+
+ pid_t mPid;
+ int mSendFd;
+ int mRecvFd;
+};
+
+std::unique_ptr<RemoteTestServerHostHolder> g_remoteTestServerHostHolder = nullptr;
+
+} // namespace
+
+void TestServerClient::InitializeOrDie(const char* filename) {
+ int sendPipeFds[2];
+ int ret = pipe(sendPipeFds);
+ LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess send pipe");
+
+ int recvPipeFds[2];
+ ret = pipe(recvPipeFds);
+ LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess recv pipe");
+
+ pid_t childPid = fork();
+ LOG_ALWAYS_FATAL_IF(childPid < 0, "Unable to fork child process");
+
+ if (childPid == 0) {
+ // We forked!
+ close(sendPipeFds[1]);
+ close(recvPipeFds[0]);
+
+ // We'll be reading from the parent's "send" and writing to the parent's "recv".
+ std::string sendPipe = std::to_string(sendPipeFds[0]);
+ std::string recvPipe = std::to_string(recvPipeFds[1]);
+ char* args[] = {
+ const_cast<char*>(filename),
+ const_cast<char*>("--test-server-host"),
+ const_cast<char*>(sendPipe.c_str()),
+ const_cast<char*>(recvPipe.c_str()),
+ nullptr,
+ };
+
+ ret = execv(filename, args);
+ ALOGE("Failed to exec libguiTestServer. ret=%d errno=%d (%s)", ret, errno, strerror(errno));
+ status_t status = -errno;
+ write(recvPipeFds[1], &status, sizeof(status));
+ _exit(EXIT_FAILURE);
+ }
+
+ close(sendPipeFds[0]);
+ close(recvPipeFds[1]);
+
+ // Check for an OK status that the host started. If so, we're good to go.
+ status_t status;
+ ret = read(recvPipeFds[0], &status, sizeof(status));
+ LOG_ALWAYS_FATAL_IF(ret != sizeof(status), "Unable to read from pipe: %d", ret);
+ LOG_ALWAYS_FATAL_IF(OK != status, "Pipe returned failed status: %d", status);
+
+ g_remoteTestServerHostHolder =
+ std::make_unique<RemoteTestServerHostHolder>(childPid, sendPipeFds[1], recvPipeFds[0]);
+}
+
+sp<TestServerClient> TestServerClient::Create() {
+ std::string serviceName = GetUniqueServiceName();
+
+ pid_t childPid = g_remoteTestServerHostHolder->CreateTestServerOrDie(serviceName);
+ ALOGD("Created child server %s with pid %d", serviceName.c_str(), childPid);
+
+ sp<libgui_test_server::ITestServer> server =
+ waitForService<libgui_test_server::ITestServer>(String16(serviceName.c_str()));
+ LOG_ALWAYS_FATAL_IF(server == nullptr);
+ ALOGD("Created connected to child server %s", serviceName.c_str());
+
+ return sp<TestServerClient>::make(server);
+}
+
+TestServerClient::TestServerClient(const sp<libgui_test_server::ITestServer>& server)
+ : mServer(server) {}
+
+TestServerClient::~TestServerClient() {
+ Kill();
+}
+
+sp<IGraphicBufferProducer> TestServerClient::CreateProducer() {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mIsAlive) {
+ return nullptr;
+ }
+
+ view::Surface surface;
+ binder::Status status = mServer->createProducer(&surface);
+
+ if (!status.isOk()) {
+ ALOGE("Failed to create remote producer. Error: %s", status.exceptionMessage().c_str());
+ return nullptr;
+ }
+
+ if (!surface.graphicBufferProducer) {
+ ALOGE("Remote producer returned no IGBP.");
+ return nullptr;
+ }
+
+ return surface.graphicBufferProducer;
+}
+
+status_t TestServerClient::Kill() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mIsAlive) {
+ return DEAD_OBJECT;
+ }
+
+ mServer->killNow();
+ mServer = nullptr;
+ mIsAlive = false;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerClient.h b/libs/gui/tests/testserver/TestServerClient.h
new file mode 100644
index 0000000..5329634
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerClient.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <libgui_test_server/ITestServer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class TestServerClient : public RefBase {
+public:
+ static void InitializeOrDie(const char* filename);
+ static sp<TestServerClient> Create();
+
+ TestServerClient(const sp<libgui_test_server::ITestServer>& server);
+ virtual ~TestServerClient() override;
+
+ sp<IGraphicBufferProducer> CreateProducer();
+ status_t Kill();
+
+private:
+ std::mutex mMutex;
+
+ sp<libgui_test_server::ITestServer> mServer;
+ bool mIsAlive = true;
+};
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerCommon.h b/libs/gui/tests/testserver/TestServerCommon.h
new file mode 100644
index 0000000..7370f20
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerCommon.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <fcntl.h>
+
+namespace android {
+
+/*
+ * Test -> TestServerHost Request to create a new ITestServer fork.
+ */
+struct CreateServerRequest {
+ /*
+ * Service name for new ITestServer.
+ */
+ char name[128];
+};
+
+/*
+ * TestServerHost -> Test Response for creating an ITestServer fork.
+ */
+struct CreateServerResponse {
+ /*
+ * pid of new ITestServer.
+ */
+ pid_t pid;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServerHost.cpp b/libs/gui/tests/testserver/TestServerHost.cpp
new file mode 100644
index 0000000..696c3b9
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerHost.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TestServerHost"
+
+#include <android-base/unique_fd.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <libgui_test_server/BnTestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <memory>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <cstddef>
+#include <cstdlib>
+
+#include "TestServerCommon.h"
+#include "TestServerHost.h"
+
+namespace android {
+
+namespace {
+
+pid_t ForkTestServer(const char* filename, char* name) {
+ pid_t childPid = fork();
+ LOG_ALWAYS_FATAL_IF(childPid == -1);
+
+ if (childPid != 0) {
+ return childPid;
+ }
+
+ // We forked!
+ const char* test_server_flag = "--test-server";
+ char* args[] = {
+ const_cast<char*>(filename),
+ const_cast<char*>(test_server_flag),
+ name,
+ nullptr,
+ };
+
+ int ret = execv(filename, args);
+ ALOGE("Failed to exec libgui_test as a TestServer. ret=%d errno=%d (%s)", ret, errno,
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+}
+
+} // namespace
+
+int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd,
+ base::unique_fd recvPipeFd) {
+ status_t status = OK;
+ LOG_ALWAYS_FATAL_IF(sizeof(status) != write(sendPipeFd.get(), &status, sizeof(status)));
+
+ ALOGE("Launched TestServerHost");
+
+ while (true) {
+ CreateServerRequest request = {};
+ ssize_t bytes = read(recvPipeFd.get(), &request, sizeof(request));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(request));
+ pid_t childPid = ForkTestServer(filename, request.name);
+
+ CreateServerResponse response = {};
+ response.pid = childPid;
+ bytes = write(sendPipeFd.get(), &response, sizeof(response));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(response));
+ }
+
+ return 0;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServerHost.h b/libs/gui/tests/testserver/TestServerHost.h
new file mode 100644
index 0000000..df22c0c
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerHost.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include <string>
+
+namespace android {
+
+/*
+ * Main method for a host process for TestServers.
+ *
+ * This must be called without any binder setup having been done, because you can't fork and do
+ * binder things once ProcessState is set up.
+ * @param filename File name of this binary / the binary to execve into
+ * @param sendPipeFd Pipe FD to send data to.
+ * @param recvPipeFd Pipe FD to receive data from.
+ * @return retcode
+ */
+int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd,
+ base::unique_fd recvPipeFd);
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl
new file mode 100644
index 0000000..c939ea0
--- /dev/null
+++ b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl
@@ -0,0 +1,12 @@
+package libgui_test_server;
+
+import android.view.Surface;
+
+// Test server for libgui_test
+interface ITestServer {
+ // Create a new producer. The server will have connected to the consumer.
+ Surface createProducer();
+
+ // Kills the server immediately.
+ void killNow();
+}
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 7c15e7c..84c2a6a 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -121,5 +121,11 @@
return str.value_or(String16());
}
+std::string Surface::toString() const {
+ std::stringstream out;
+ out << name;
+ return out.str();
+}
+
} // namespace view
} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index d782f42..e4e81ad 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,6 +30,7 @@
"android/os/InputEventInjectionResult.aidl",
"android/os/InputEventInjectionSync.aidl",
"android/os/InputConfig.aidl",
+ "android/os/MotionEventFlag.aidl",
"android/os/PointerIconType.aidl",
],
}
@@ -231,6 +232,7 @@
"MotionPredictorMetricsManager.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
+ "Resampler.cpp",
"TfLiteMotionPredictor.cpp",
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
@@ -257,6 +259,7 @@
],
shared_libs: [
+ "android.companion.virtualdevice.flags-aconfig-cc",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index b098147..a2bb345 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -580,7 +580,7 @@
&pointerProperties[pointerCount]);
mSampleEventTimes.clear();
mSamplePointerCoords.clear();
- addSample(eventTime, pointerCoords);
+ addSample(eventTime, pointerCoords, mId);
}
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
@@ -640,9 +640,9 @@
mSampleEventTimes = other.mSampleEventTimes;
}
-void MotionEvent::addSample(
- int64_t eventTime,
- const PointerCoords* pointerCoords) {
+void MotionEvent::addSample(int64_t eventTime, const PointerCoords* pointerCoords,
+ int32_t eventId) {
+ mId = eventId;
mSampleEventTimes.push_back(eventTime);
mSamplePointerCoords.insert(mSamplePointerCoords.end(), &pointerCoords[0],
&pointerCoords[getPointerCount()]);
diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp
index dce528f..1eeb4e6 100644
--- a/libs/input/InputConsumer.cpp
+++ b/libs/input/InputConsumer.cpp
@@ -135,7 +135,7 @@
}
event.setMetaState(event.getMetaState() | msg.body.motion.metaState);
- event.addSample(msg.body.motion.eventTime, pointerCoords);
+ event.addSample(msg.body.motion.eventTime, pointerCoords, msg.body.motion.eventId);
}
void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) {
@@ -697,7 +697,7 @@
currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
}
- event->addSample(sampleTime, touchState.lastResample.pointers);
+ event->addSample(sampleTime, touchState.lastResample.pointers, event->getId());
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index e193983..cdbc186 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "InputTransport"
+#define LOG_TAG "InputConsumerNoResampling"
#define ATRACE_TAG ATRACE_TAG_INPUT
+#include <chrono>
+
#include <inttypes.h>
#include <android-base/logging.h>
@@ -31,12 +33,12 @@
#include <input/PrintTools.h>
#include <input/TraceTools.h>
-namespace input_flags = com::android::input::flags;
-
namespace android {
namespace {
+using std::chrono::nanoseconds;
+
/**
* Log debug messages relating to the consumer end of the transport channel.
* Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
@@ -114,7 +116,7 @@
// TODO(b/329770983): figure out if it's safe to combine events with mismatching metaState
event.setMetaState(event.getMetaState() | msg.body.motion.metaState);
- event.addSample(msg.body.motion.eventTime, pointerCoords.data());
+ event.addSample(msg.body.motion.eventTime, pointerCoords.data(), msg.body.motion.eventId);
}
std::unique_ptr<TouchModeEvent> createTouchModeEvent(const InputMessage& msg) {
@@ -168,17 +170,24 @@
return msg;
}
+bool isPointerEvent(const MotionEvent& motionEvent) {
+ return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
+}
} // namespace
using android::base::Result;
-using android::base::StringPrintf;
// --- InputConsumerNoResampling ---
InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
sp<Looper> looper,
- InputConsumerCallbacks& callbacks)
- : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) {
+ InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler)
+ : mChannel{channel},
+ mLooper{looper},
+ mCallbacks{callbacks},
+ mResampler{std::move(resampler)},
+ mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
std::bind(&InputConsumerNoResampling::handleReceiveCallback, this,
@@ -190,7 +199,7 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- consumeBatchedInputEvents(std::nullopt);
+ consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
while (!mOutboundQueue.empty()) {
processOutboundEvents();
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
@@ -216,8 +225,7 @@
int handledEvents = 0;
if (events & ALOOPER_EVENT_INPUT) {
- std::vector<InputMessage> messages = readAllMessages();
- handleMessages(std::move(messages));
+ handleMessages(readAllMessages());
handledEvents |= ALOOPER_EVENT_INPUT;
}
@@ -325,10 +333,8 @@
// add it to batch
mBatches[deviceId].emplace(msg);
} else {
- // consume all pending batches for this event immediately
- // TODO(b/329776327): figure out if this could be smarter by limiting the
- // consumption only to the current device.
- consumeBatchedInputEvents(std::nullopt);
+ // consume all pending batches for this device immediately
+ consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
handleMessage(msg);
}
} else {
@@ -445,51 +451,81 @@
}
}
+std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
+InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
+ std::queue<InputMessage>& messages) {
+ std::unique_ptr<MotionEvent> motionEvent;
+ std::optional<uint32_t> firstSeqForBatch;
+ const nanoseconds resampleLatency =
+ (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0};
+ const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
+
+ while (!messages.empty() &&
+ (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
+ if (motionEvent == nullptr) {
+ motionEvent = createMotionEvent(messages.front());
+ firstSeqForBatch = messages.front().header.seq;
+ const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}});
+ LOG_IF(FATAL, !inserted)
+ << "The sequence " << messages.front().header.seq << " was already present!";
+ } else {
+ addSample(*motionEvent, messages.front());
+ mBatchedSequenceNumbers[*firstSeqForBatch].push_back(messages.front().header.seq);
+ }
+ messages.pop();
+ }
+ // Check if resampling should be performed.
+ if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) {
+ InputMessage* futureSample = nullptr;
+ if (!messages.empty()) {
+ futureSample = &messages.front();
+ }
+ mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
+ futureSample);
+ }
+ return std::make_pair(std::move(motionEvent), firstSeqForBatch);
+}
+
bool InputConsumerNoResampling::consumeBatchedInputEvents(
- std::optional<nsecs_t> requestedFrameTime) {
+ std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
ensureCalledOnLooperThread(__func__);
// When batching is not enabled, we want to consume all events. That's equivalent to having an
- // infinite frameTime.
- const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
+ // infinite requestedFrameTime.
+ requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
bool producedEvents = false;
- for (auto& [deviceId, messages] : mBatches) {
- std::unique_ptr<MotionEvent> motion;
- std::optional<uint32_t> firstSeqForBatch;
- std::vector<uint32_t> sequences;
- while (!messages.empty()) {
- const InputMessage& msg = messages.front();
- if (msg.body.motion.eventTime > frameTime) {
- break;
- }
- if (motion == nullptr) {
- motion = createMotionEvent(msg);
- firstSeqForBatch = msg.header.seq;
- const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}});
- if (!inserted) {
- LOG(FATAL) << "The sequence " << msg.header.seq << " was already present!";
- }
- } else {
- addSample(*motion, msg);
- mBatchedSequenceNumbers[*firstSeqForBatch].push_back(msg.header.seq);
- }
- messages.pop();
- }
+
+ for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
+ : (mBatches.begin());
+ deviceIdIter != mBatches.cend(); ++deviceIdIter) {
+ std::queue<InputMessage>& messages = deviceIdIter->second;
+ auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
if (motion != nullptr) {
LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
producedEvents = true;
} else {
- // This is OK, it just means that the frameTime is too old (all events that we have
- // pending are in the future of the frametime). Maybe print a
- // warning? If there are multiple devices active though, this might be normal and can
- // just be ignored, unless none of them resulted in any consumption (in that case, this
- // function would already return "false" so we could just leave it up to the caller).
+ // This is OK, it just means that the requestedFrameTime is too old (all events that we
+ // have pending are in the future of the requestedFrameTime). Maybe print a warning? If
+ // there are multiple devices active though, this might be normal and can just be
+ // ignored, unless none of them resulted in any consumption (in that case, this function
+ // would already return "false" so we could just leave it up to the caller).
+ }
+
+ if (deviceId.has_value()) {
+ // We already consumed events for this device. Break here to prevent iterating over the
+ // other devices.
+ break;
}
}
std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); });
return producedEvents;
}
+bool InputConsumerNoResampling::consumeBatchedInputEvents(
+ std::optional<nsecs_t> requestedFrameTime) {
+ return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime);
+}
+
void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const {
sp<Looper> callingThreadLooper = Looper::getForThread();
if (callingThreadLooper != mLooper) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 9333ab8..c903031 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <ctype.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <ftl/enum.h>
@@ -31,6 +32,9 @@
namespace android {
+// Set to true to log detailed debugging messages about IDC file probing.
+static constexpr bool DEBUG_PROBE = false;
+
static const char* CONFIGURATION_FILE_DIR[] = {
"idc/",
"keylayout/",
@@ -114,15 +118,18 @@
for (const auto& prefix : pathPrefixes) {
path = prefix;
appendInputDeviceConfigurationFileRelativePath(path, name, type);
-#if DEBUG_PROBE
- ALOGD("Probing for system provided input device configuration file: path='%s'",
- path.c_str());
-#endif
if (!access(path.c_str(), R_OK)) {
-#if DEBUG_PROBE
- ALOGD("Found");
-#endif
+ LOG_IF(INFO, DEBUG_PROBE)
+ << "Found system-provided input device configuration file at " << path;
return path;
+ } else if (errno != ENOENT) {
+ LOG(WARNING) << "Couldn't find a system-provided input device configuration file at "
+ << path << " due to error " << errno << " (" << strerror(errno)
+ << "); there may be an IDC file there that cannot be loaded.";
+ } else {
+ LOG_IF(ERROR, DEBUG_PROBE)
+ << "Didn't find system-provided input device configuration file at " << path
+ << ": " << strerror(errno);
}
}
@@ -135,21 +142,22 @@
}
path += "/system/devices/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
-#if DEBUG_PROBE
- ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str());
-#endif
if (!access(path.c_str(), R_OK)) {
-#if DEBUG_PROBE
- ALOGD("Found");
-#endif
+ LOG_IF(INFO, DEBUG_PROBE) << "Found system user input device configuration file at "
+ << path;
return path;
+ } else if (errno != ENOENT) {
+ LOG(WARNING) << "Couldn't find a system user input device configuration file at " << path
+ << " due to error " << errno << " (" << strerror(errno)
+ << "); there may be an IDC file there that cannot be loaded.";
+ } else {
+ LOG_IF(ERROR, DEBUG_PROBE) << "Didn't find system user input device configuration file at "
+ << path << ": " << strerror(errno);
}
// Not found.
-#if DEBUG_PROBE
- ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
- name.c_str(), type);
-#endif
+ LOG_IF(INFO, DEBUG_PROBE) << "Probe failed to find input device configuration file with name '"
+ << name << "' and type " << ftl::enum_string(type);
return "";
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index bac681d..77dcaa9 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -375,13 +375,11 @@
sp<IBinder> token = sp<BBinder>::make();
- std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
- outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
+ outServerChannel = InputChannel::create(name, std::move(serverFd), token);
- std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
- outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
+ outClientChannel = InputChannel::create(name, std::move(clientFd), token);
return OK;
}
@@ -590,7 +588,8 @@
mInputVerifier.processMovement(deviceId, source, action, pointerCount,
pointerProperties, pointerCoords, flags);
if (!result.ok()) {
- LOG(FATAL) << "Bad stream: " << result.error();
+ LOG(ERROR) << "Bad stream: " << result.error();
+ return BAD_VALUE;
}
}
if (debugTransportPublisher()) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 1cf5612..b0563ab 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -317,19 +317,8 @@
return true;
}
-void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
- if (fromKeyCode == toKeyCode) {
- mKeyRemapping.erase(fromKeyCode);
-#if DEBUG_MAPPING
- ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
-#endif
- return;
- }
- mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
-#if DEBUG_MAPPING
- ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
- toKeyCode);
-#endif
+void KeyCharacterMap::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) {
+ mKeyRemapping = keyRemapping;
}
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 5088188..f3241c9 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -240,8 +240,9 @@
std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const {
std::vector<int32_t> scanCodes;
+ // b/354333072: Only consider keys without FUNCTION flag
for (const auto& [scanCode, key] : mKeysByScanCode) {
- if (keyCode == key.keyCode) {
+ if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FUNCTION)) {
scanCodes.push_back(scanCode);
}
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 5b61d39..c61d394 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -72,9 +72,13 @@
// --- JerkTracker ---
-JerkTracker::JerkTracker(bool normalizedDt) : mNormalizedDt(normalizedDt) {}
+JerkTracker::JerkTracker(bool normalizedDt, float alpha)
+ : mNormalizedDt(normalizedDt), mAlpha(alpha) {}
void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) {
+ // If we previously had full samples, we have a previous jerk calculation
+ // to do weighted smoothing.
+ const bool applySmoothing = mTimestamps.size() == mTimestamps.capacity();
mTimestamps.pushBack(timestamp);
const int numSamples = mTimestamps.size();
@@ -115,6 +119,16 @@
}
}
+ if (numSamples == static_cast<int>(mTimestamps.capacity())) {
+ float newJerkMagnitude = std::hypot(newXDerivatives[3], newYDerivatives[3]);
+ ALOGD_IF(isDebug(), "raw jerk: %f", newJerkMagnitude);
+ if (applySmoothing) {
+ mJerkMagnitude = mJerkMagnitude + (mAlpha * (newJerkMagnitude - mJerkMagnitude));
+ } else {
+ mJerkMagnitude = newJerkMagnitude;
+ }
+ }
+
std::swap(newXDerivatives, mXDerivatives);
std::swap(newYDerivatives, mYDerivatives);
}
@@ -125,7 +139,7 @@
std::optional<float> JerkTracker::jerkMagnitude() const {
if (mTimestamps.size() == mTimestamps.capacity()) {
- return std::hypot(mXDerivatives[3], mYDerivatives[3]);
+ return mJerkMagnitude;
}
return std::nullopt;
}
@@ -139,6 +153,24 @@
mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)),
mReportAtomFunction(reportAtomFunction) {}
+void MotionPredictor::initializeObjects() {
+ mModel = TfLiteMotionPredictorModel::create();
+ LOG_ALWAYS_FATAL_IF(!mModel);
+
+ // mJerkTracker assumes normalized dt = 1 between recorded samples because
+ // the underlying mModel input also assumes fixed-interval samples.
+ // Normalized dt as 1 is also used to correspond with the similar Jank
+ // implementation from the JetPack MotionPredictor implementation.
+ mJerkTracker = std::make_unique<JerkTracker>(/*normalizedDt=*/true, mModel->config().jerkAlpha);
+
+ mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+
+ mMetricsManager =
+ std::make_unique<MotionPredictorMetricsManager>(mModel->config().predictionInterval,
+ mModel->outputLength(),
+ mReportAtomFunction);
+}
+
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
// We still have an active gesture for another device. The provided MotionEvent is not
@@ -155,28 +187,18 @@
return {};
}
- // Initialise the model now that it's likely to be used.
if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create();
- LOG_ALWAYS_FATAL_IF(!mModel);
- }
-
- if (!mBuffers) {
- mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+ initializeObjects();
}
// Pass input event to the MetricsManager.
- if (!mMetricsManager) {
- mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength(),
- mReportAtomFunction);
- }
mMetricsManager->onRecord(event);
const int32_t action = event.getActionMasked();
if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
mBuffers->reset();
- mJerkTracker.reset();
+ mJerkTracker->reset();
mLastEvent.reset();
return {};
} else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
@@ -211,9 +233,9 @@
0, i),
.orientation = event.getHistoricalOrientation(0, i),
});
- mJerkTracker.pushSample(event.getHistoricalEventTime(i),
- coords->getAxisValue(AMOTION_EVENT_AXIS_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ mJerkTracker->pushSample(event.getHistoricalEventTime(i),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
}
if (!mLastEvent) {
@@ -261,7 +283,7 @@
int64_t predictionTime = mBuffers->lastTimestamp();
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
- const float jerkMagnitude = mJerkTracker.jerkMagnitude().value_or(0);
+ const float jerkMagnitude = mJerkTracker->jerkMagnitude().value_or(0);
const float fractionKept =
1 - normalizeRange(jerkMagnitude, mModel->config().lowJerk, mModel->config().highJerk);
// float to ensure proper division below.
@@ -323,7 +345,7 @@
event.getRawTransform(), event.getDownTime(), predictionTime,
event.getPointerCount(), event.getPointerProperties(), &coords);
} else {
- prediction->addSample(predictionTime, &coords);
+ prediction->addSample(predictionTime, &coords, prediction->getId());
}
axisFrom = axisTo;
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
new file mode 100644
index 0000000..51fadf8
--- /dev/null
+++ b/libs/input/Resampler.cpp
@@ -0,0 +1,266 @@
+/**
+ * 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.
+ */
+
+#define LOG_TAG "LegacyResampler"
+
+#include <algorithm>
+#include <chrono>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <ftl/enum.h>
+
+#include <input/Resampler.h>
+#include <utils/Timers.h>
+
+using std::chrono::nanoseconds;
+
+namespace android {
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+bool debugResampling() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
+ ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_RESAMPLING;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+}
+
+constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
+constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
+
+bool canResampleTool(ToolType toolType) {
+ return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
+ toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
+}
+
+inline float lerp(float a, float b, float alpha) {
+ return a + alpha * (b - a);
+}
+
+PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
+ float alpha) {
+ // We use the value of alpha to initialize resampledCoords with the latest sample information.
+ PointerCoords resampledCoords = (alpha < 1.0f) ? a : b;
+ resampledCoords.isResampled = true;
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
+ return resampledCoords;
+}
+} // namespace
+
+void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ const size_t latestIndex = numSamples - 1;
+ const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
+ for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
+ std::vector<Pointer> pointers;
+ const size_t numPointers = motionEvent.getPointerCount();
+ for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
+ // getSamplePointerCoords is the vector representation of a getHistorySize by
+ // getPointerCount matrix.
+ const PointerCoords& pointerCoords =
+ motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
+ pointers.push_back(
+ Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+ }
+ mLatestSamples.pushBack(
+ Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
+ }
+}
+
+LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
+ std::vector<Pointer> pointers;
+ for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
+ pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
+ message.body.motion.pointers[i].coords});
+ }
+ return Sample{nanoseconds{message.body.motion.eventTime}, pointers};
+}
+
+bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
+ if (target.pointers.size() > auxiliary.pointers.size()) {
+ LOG_IF(INFO, debugResampling())
+ << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
+ return false;
+ }
+ for (size_t i = 0; i < target.pointers.size(); ++i) {
+ if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
+ return false;
+ }
+ if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
+ return false;
+ }
+ if (!canResampleTool(target.pointers[i].properties.toolType)) {
+ LOG_IF(INFO, debugResampling())
+ << "Not resampled. Cannot resample "
+ << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LegacyResampler::canInterpolate(const InputMessage& message) const {
+ LOG_IF(FATAL, mLatestSamples.empty())
+ << "Not resampled. mLatestSamples must not be empty to interpolate.";
+
+ const Sample& pastSample = *(mLatestSamples.end() - 1);
+ const Sample& futureSample = messageToSample(message);
+
+ if (!pointerPropertiesResampleable(pastSample, futureSample)) {
+ return false;
+ }
+
+ const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return false;
+ }
+ return true;
+}
+
+std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
+ nanoseconds resampleTime, const InputMessage& futureSample) const {
+ if (!canInterpolate(futureSample)) {
+ return std::nullopt;
+ }
+ LOG_IF(FATAL, mLatestSamples.empty())
+ << "Not resampled. mLatestSamples must not be empty to interpolate.";
+
+ const Sample& pastSample = *(mLatestSamples.end() - 1);
+
+ const nanoseconds delta =
+ nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
+ const float alpha =
+ std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
+
+ std::vector<Pointer> resampledPointers;
+ for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
+ const PointerCoords& resampledCoords =
+ calculateResampledCoords(pastSample.pointers[i].coords,
+ futureSample.body.motion.pointers[i].coords, alpha);
+ resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
+ }
+ return Sample{resampleTime, resampledPointers};
+}
+
+bool LegacyResampler::canExtrapolate() const {
+ if (mLatestSamples.size() < 2) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
+ return false;
+ }
+
+ const Sample& pastSample = *(mLatestSamples.end() - 2);
+ const Sample& presentSample = *(mLatestSamples.end() - 1);
+
+ if (!pointerPropertiesResampleable(presentSample, pastSample)) {
+ return false;
+ }
+
+ const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return false;
+ } else if (delta > RESAMPLE_MAX_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
+ return false;
+ }
+ return true;
+}
+
+std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation(
+ nanoseconds resampleTime) const {
+ if (!canExtrapolate()) {
+ return std::nullopt;
+ }
+ LOG_IF(FATAL, mLatestSamples.size() < 2)
+ << "Not resampled. mLatestSamples must have at least two samples to extrapolate.";
+
+ const Sample& pastSample = *(mLatestSamples.end() - 2);
+ const Sample& presentSample = *(mLatestSamples.end() - 1);
+
+ const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
+ // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
+ // we use this value as the resample time target.
+ const nanoseconds farthestPrediction =
+ presentSample.eventTime + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
+ const nanoseconds newResampleTime =
+ (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
+ LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
+ << "Resample time is too far in the future. Adjusting prediction from "
+ << (resampleTime - presentSample.eventTime) << " to "
+ << (farthestPrediction - presentSample.eventTime) << "ns.";
+ const float alpha =
+ std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
+ delta;
+
+ std::vector<Pointer> resampledPointers;
+ for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
+ const PointerCoords& resampledCoords =
+ calculateResampledCoords(pastSample.pointers[i].coords,
+ presentSample.pointers[i].coords, alpha);
+ resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
+ }
+ return Sample{newResampleTime, resampledPointers};
+}
+
+inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
+ MotionEvent& motionEvent) {
+ motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(),
+ motionEvent.getId());
+}
+
+nanoseconds LegacyResampler::getResampleLatency() const {
+ return RESAMPLE_LATENCY;
+}
+
+void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) {
+ if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
+ mLatestSamples.clear();
+ }
+ mPreviousDeviceId = motionEvent.getDeviceId();
+
+ const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
+
+ updateLatestSamples(motionEvent);
+
+ const std::optional<Sample> sample = (futureSample != nullptr)
+ ? (attemptInterpolation(resampleTime, *futureSample))
+ : (attemptExtrapolation(resampleTime));
+ if (sample.has_value()) {
+ addSampleToMotionEvent(*sample, motionEvent);
+ }
+}
+} // namespace android
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index b843a4b..5250a9d 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -283,6 +283,7 @@
.distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
.lowJerk = parseXMLFloat(*configRoot, "low-jerk"),
.highJerk = parseXMLFloat(*configRoot, "high-jerk"),
+ .jerkAlpha = parseXMLFloat(*configRoot, "jerk-alpha"),
};
return std::unique_ptr<TfLiteMotionPredictorModel>(
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index eea06f1..51edbf1 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -16,30 +16,267 @@
#define LOG_TAG "VirtualInputDevice"
+#include <android-base/logging.h>
#include <android/input.h>
#include <android/keycodes.h>
+#include <android_companion_virtualdevice_flags.h>
#include <fcntl.h>
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
#include <linux/uinput.h>
-#include <math.h>
-#include <utils/Log.h>
-#include <map>
#include <string>
using android::base::unique_fd;
+namespace {
+
/**
* Log debug messages about native virtual input devices.
* Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG"
*/
-static bool isDebug() {
+bool isDebug() {
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
}
+unique_fd invalidFd() {
+ return unique_fd(-1);
+}
+
+} // namespace
+
namespace android {
+namespace vd_flags = android::companion::virtualdevice::flags;
+
+/** Creates a new uinput device and assigns a file descriptor. */
+unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth) {
+ unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
+ if (fd < 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+
+ ioctl(fd, UI_SET_PHYS, phys);
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+ switch (deviceType) {
+ case DeviceType::DPAD:
+ for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::KEYBOARD:
+ for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::MOUSE:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
+ ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
+ ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
+ ioctl(fd, UI_SET_RELBIT, REL_X);
+ ioctl(fd, UI_SET_RELBIT, REL_Y);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
+ }
+ break;
+ case DeviceType::TOUCHSCREEN:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ break;
+ case DeviceType::STYLUS:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
+ ioctl(fd, UI_SET_ABSBIT, ABS_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ break;
+ case DeviceType::ROTARY_ENCODER:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
+ }
+ break;
+ default:
+ ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
+ return invalidFd();
+ }
+
+ int version;
+ if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
+ uinput_setup setup;
+ memset(&setup, 0, sizeof(setup));
+ std::strncpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
+ setup.id.version = 1;
+ setup.id.bustype = BUS_VIRTUAL;
+ setup.id.vendor = vendorId;
+ setup.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_MT_POSITION_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_MT_POSITION_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup majorAbsSetup;
+ majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
+ majorAbsSetup.absinfo.maximum = screenWidth - 1;
+ majorAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_MT_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup slotAbsSetup;
+ slotAbsSetup.code = ABS_MT_SLOT;
+ slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ slotAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup trackingIdAbsSetup;
+ trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
+ trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ trackingIdAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
+ return invalidFd();
+ }
+ } else if (deviceType == DeviceType::STYLUS) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup tiltXAbsSetup;
+ tiltXAbsSetup.code = ABS_TILT_X;
+ tiltXAbsSetup.absinfo.maximum = 90;
+ tiltXAbsSetup.absinfo.minimum = -90;
+ if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup tiltYAbsSetup;
+ tiltYAbsSetup.code = ABS_TILT_Y;
+ tiltYAbsSetup.absinfo.maximum = 90;
+ tiltYAbsSetup.absinfo.minimum = -90;
+ if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ }
+ if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+ } else {
+ // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
+ ALOGI("Falling back to version %d manual setup", version);
+ uinput_user_dev fallback;
+ memset(&fallback, 0, sizeof(fallback));
+ std::strncpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
+ fallback.id.version = 1;
+ fallback.id.bustype = BUS_VIRTUAL;
+ fallback.id.vendor = vendorId;
+ fallback.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ fallback.absmin[ABS_MT_POSITION_X] = 0;
+ fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
+ fallback.absmin[ABS_MT_POSITION_Y] = 0;
+ fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
+ fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
+ fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
+ fallback.absmin[ABS_MT_PRESSURE] = 0;
+ fallback.absmax[ABS_MT_PRESSURE] = 255;
+ } else if (deviceType == DeviceType::STYLUS) {
+ fallback.absmin[ABS_X] = 0;
+ fallback.absmax[ABS_X] = screenWidth - 1;
+ fallback.absmin[ABS_Y] = 0;
+ fallback.absmax[ABS_Y] = screenHeight - 1;
+ fallback.absmin[ABS_TILT_X] = -90;
+ fallback.absmax[ABS_TILT_X] = 90;
+ fallback.absmin[ABS_TILT_Y] = -90;
+ fallback.absmax[ABS_TILT_Y] = 90;
+ fallback.absmin[ABS_PRESSURE] = 0;
+ fallback.absmax[ABS_PRESSURE] = 255;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+ }
+
+ if (ioctl(fd, UI_DEV_CREATE) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+
+ return fd;
+}
+
VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
VirtualInputDevice::~VirtualInputDevice() {
@@ -253,7 +490,10 @@
// clang-format on
};
-VirtualMouse::VirtualMouse(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+VirtualMouse::VirtualMouse(unique_fd fd)
+ : VirtualInputDevice(std::move(fd)),
+ mAccumulatedHighResScrollX(0),
+ mAccumulatedHighResScrollY(0) {}
VirtualMouse::~VirtualMouse() {}
@@ -272,9 +512,47 @@
bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement,
std::chrono::nanoseconds eventTime) {
- return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement, eventTime) &&
- writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement, eventTime) &&
- writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ if (!vd_flags::high_resolution_scroll()) {
+ return writeInputEvent(EV_REL, REL_HWHEEL, static_cast<int32_t>(xAxisMovement),
+ eventTime) &&
+ writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(yAxisMovement),
+ eventTime) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ }
+
+ const auto highResScrollX =
+ static_cast<int32_t>(xAxisMovement * kEvdevHighResScrollUnitsPerDetent);
+ const auto highResScrollY =
+ static_cast<int32_t>(yAxisMovement * kEvdevHighResScrollUnitsPerDetent);
+ bool highResScrollResult =
+ writeInputEvent(EV_REL, REL_HWHEEL_HI_RES, highResScrollX, eventTime) &&
+ writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollY, eventTime);
+ if (!highResScrollResult) {
+ return false;
+ }
+
+ // According to evdev spec, a high-resolution mouse needs to emit REL_WHEEL / REL_HWHEEL events
+ // in addition to high-res scroll events. Regular scroll events can approximate high-res scroll
+ // events, so we send a regular scroll event when the accumulated scroll motion reaches a detent
+ // (single mouse wheel click).
+ mAccumulatedHighResScrollX += highResScrollX;
+ mAccumulatedHighResScrollY += highResScrollY;
+ const int32_t scrollX = mAccumulatedHighResScrollX / kEvdevHighResScrollUnitsPerDetent;
+ const int32_t scrollY = mAccumulatedHighResScrollY / kEvdevHighResScrollUnitsPerDetent;
+ if (scrollX != 0) {
+ if (!writeInputEvent(EV_REL, REL_HWHEEL, scrollX, eventTime)) {
+ return false;
+ }
+ mAccumulatedHighResScrollX %= kEvdevHighResScrollUnitsPerDetent;
+ }
+ if (scrollY != 0) {
+ if (!writeInputEvent(EV_REL, REL_WHEEL, scrollY, eventTime)) {
+ return false;
+ }
+ mAccumulatedHighResScrollY %= kEvdevHighResScrollUnitsPerDetent;
+ }
+
+ return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
}
// --- VirtualTouchscreen ---
@@ -509,4 +787,39 @@
return true;
}
+// --- VirtualRotaryEncoder ---
+VirtualRotaryEncoder::VirtualRotaryEncoder(unique_fd fd)
+ : VirtualInputDevice(std::move(fd)), mAccumulatedHighResScrollAmount(0) {}
+
+VirtualRotaryEncoder::~VirtualRotaryEncoder() {}
+
+bool VirtualRotaryEncoder::writeScrollEvent(float scrollAmount,
+ std::chrono::nanoseconds eventTime) {
+ if (!vd_flags::high_resolution_scroll()) {
+ return writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(scrollAmount), eventTime) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ }
+
+ const auto highResScrollAmount =
+ static_cast<int32_t>(scrollAmount * kEvdevHighResScrollUnitsPerDetent);
+ if (!writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollAmount, eventTime)) {
+ return false;
+ }
+
+ // According to evdev spec, a high-resolution scroll device needs to emit REL_WHEEL / REL_HWHEEL
+ // events in addition to high-res scroll events. Regular scroll events can approximate high-res
+ // scroll events, so we send a regular scroll event when the accumulated scroll motion reaches a
+ // detent (single wheel click).
+ mAccumulatedHighResScrollAmount += highResScrollAmount;
+ const int32_t scroll = mAccumulatedHighResScrollAmount / kEvdevHighResScrollUnitsPerDetent;
+ if (scroll != 0) {
+ if (!writeInputEvent(EV_REL, REL_WHEEL, scroll, eventTime)) {
+ return false;
+ }
+ mAccumulatedHighResScrollAmount %= kEvdevHighResScrollUnitsPerDetent;
+ }
+
+ return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+}
+
} // namespace android
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index a77dfa5..e23fc94 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -49,130 +49,24 @@
const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
/**
- * This flag indicates that the window that received this motion event is partly
- * or wholly obscured by another visible window above it and the event directly passed through
- * the obscured area.
- *
- * A security sensitive application can check this flag to identify situations in which
- * a malicious application may have covered up part of its content for the purpose
- * of misleading the user or hijacking touches. An appropriate response might be
- * to drop the suspect touches or to take additional precautions to confirm the user's
- * actual intent.
- */
- const int MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1;
-
- /**
- * This flag indicates that the window that received this motion event is partly
- * or wholly obscured by another visible window above it and the event did not directly pass
- * through the obscured area.
- *
- * A security sensitive application can check this flag to identify situations in which
- * a malicious application may have covered up part of its content for the purpose
- * of misleading the user or hijacking touches. An appropriate response might be
- * to drop the suspect touches or to take additional precautions to confirm the user's
- * actual intent.
- *
- * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is
- * obstructed in areas other than the touched location.
- */
- const int MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
-
- /**
- * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
- * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to
- * prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
- * @hide
- */
- const int MOTION_EVENT_FLAG_HOVER_EXIT_PENDING = 0x4;
-
- /**
- * This flag indicates that the event has been generated by a gesture generator. It
- * provides a hint to the GestureDetector to not apply any touch slop.
- *
- * @hide
- */
- const int MOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8;
-
- /**
- * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
- * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
- * is set, the typical actions that occur in response for a pointer going up (such as click
- * handlers, end of drawing) should be aborted. This flag is typically set when the user was
- * accidentally touching the screen, such as by gripping the device, or placing the palm on the
- * screen.
- *
- * @see #ACTION_POINTER_UP
- * @see #ACTION_CANCEL
+ * Common input event flag used for both motion and key events for a gesture or pointer being
+ * canceled.
*/
const int INPUT_EVENT_FLAG_CANCELED = 0x20;
/**
- * This flag indicates that the event will not cause a focus change if it is directed to an
- * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer
- * gestures to allow the user to direct gestures to an unfocused window without bringing the
- * window into focus.
- * @hide
- */
- const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40;
-
- /**
- * This flag indicates that the event has a valid value for AXIS_ORIENTATION.
- *
- * This is a private flag that is not used in Java.
- * @hide
- */
- const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80;
-
- /**
- * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine
- * the direction in which the tool is pointing. The value of the orientation axis will be in
- * the range [-pi, pi], which represents a full circle. This is usually supported by devices
- * like styluses.
- *
- * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing
- * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2],
- * which represents half a circle. This is usually the case for devices like touchscreens and
- * touchpads, for which it is difficult to tell which direction along the major axis of the
- * touch ellipse the finger is pointing.
- *
- * This is a private flag that is not used in Java.
- * @hide
- */
- const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100;
-
- /**
- * The input event was generated or modified by accessibility service.
- * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either
- * set of flags, including in input/Input.h and in android/input.h.
+ * Common input event flag used for both motion and key events, indicating that the event
+ * was generated or modified by accessibility service.
*/
const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
/**
- * Private flag that indicates when the system has detected that this motion event
- * may be inconsistent with respect to the sequence of previously delivered motion events,
- * such as when a pointer move event is sent but the pointer is not down.
- *
- * @hide
- * @see #isTainted
- * @see #setTainted
+ * Common input event flag used for both motion and key events, indicating that the system has
+ * detected this event may be inconsistent with the current event sequence or gesture, such as
+ * when a pointer move event is sent but the pointer is not down.
*/
const int INPUT_EVENT_FLAG_TAINTED = 0x80000000;
- /**
- * Private flag indicating that this event was synthesized by the system and should be delivered
- * to the accessibility focused view first. When being dispatched such an event is not handled
- * by predecessors of the accessibility focused view and after the event reaches that view the
- * flag is cleared and normal event dispatch is performed. This ensures that the platform can
- * click on any view that has accessibility focus which is semantically equivalent to asking the
- * view to perform a click accessibility action but more generic as views not implementing click
- * action correctly can still be activated.
- *
- * @hide
- * @see #isTargetAccessibilityFocus()
- * @see #setTargetAccessibilityFocus(boolean)
- */
- const int MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
-
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
diff --git a/libs/input/android/os/MotionEventFlag.aidl b/libs/input/android/os/MotionEventFlag.aidl
new file mode 100644
index 0000000..2093b06
--- /dev/null
+++ b/libs/input/android/os/MotionEventFlag.aidl
@@ -0,0 +1,152 @@
+/**
+ * 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 android.os;
+
+import android.os.IInputConstants;
+
+/**
+ * Flag definitions for MotionEvents.
+ * @hide
+ */
+@Backing(type="int")
+enum MotionEventFlag {
+
+ /**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it and the event directly passed through
+ * the obscured area.
+ *
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ */
+ WINDOW_IS_OBSCURED = 0x1,
+
+ /**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it and the event did not directly pass
+ * through the obscured area.
+ *
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ *
+ * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is
+ * obstructed in areas other than the touched location.
+ */
+ WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
+
+ /**
+ * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
+ * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to
+ * prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
+ * @hide
+ */
+ HOVER_EXIT_PENDING = 0x4,
+
+ /**
+ * This flag indicates that the event has been generated by a gesture generator. It
+ * provides a hint to the GestureDetector to not apply any touch slop.
+ *
+ * @hide
+ */
+ IS_GENERATED_GESTURE = 0x8,
+
+ /**
+ * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
+ * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
+ * is set, the typical actions that occur in response for a pointer going up (such as click
+ * handlers, end of drawing) should be aborted. This flag is typically set when the user was
+ * accidentally touching the screen, such as by gripping the device, or placing the palm on the
+ * screen.
+ *
+ * @see #ACTION_POINTER_UP
+ * @see #ACTION_CANCEL
+ */
+ CANCELED = IInputConstants.INPUT_EVENT_FLAG_CANCELED,
+
+ /**
+ * This flag indicates that the event will not cause a focus change if it is directed to an
+ * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer
+ * gestures to allow the user to direct gestures to an unfocused window without bringing the
+ * window into focus.
+ * @hide
+ */
+ NO_FOCUS_CHANGE = 0x40,
+
+ /**
+ * This flag indicates that the event has a valid value for AXIS_ORIENTATION.
+ *
+ * This is a private flag that is not used in Java.
+ * @hide
+ */
+ PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80,
+
+ /**
+ * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine
+ * the direction in which the tool is pointing. The value of the orientation axis will be in
+ * the range [-pi, pi], which represents a full circle. This is usually supported by devices
+ * like styluses.
+ *
+ * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing
+ * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2],
+ * which represents half a circle. This is usually the case for devices like touchscreens and
+ * touchpads, for which it is difficult to tell which direction along the major axis of the
+ * touch ellipse the finger is pointing.
+ *
+ * This is a private flag that is not used in Java.
+ * @hide
+ */
+ PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100,
+
+ /**
+ * The input event was generated or modified by accessibility service.
+ * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either
+ * set of flags, including in input/Input.h and in android/input.h.
+ */
+ IS_ACCESSIBILITY_EVENT = IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+
+ /**
+ * Private flag that indicates when the system has detected that this motion event
+ * may be inconsistent with respect to the sequence of previously delivered motion events,
+ * such as when a pointer move event is sent but the pointer is not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ TAINTED = IInputConstants.INPUT_EVENT_FLAG_TAINTED,
+
+ /**
+ * Private flag indicating that this event was synthesized by the system and should be delivered
+ * to the accessibility focused view first. When being dispatched such an event is not handled
+ * by predecessors of the accessibility focused view and after the event reaches that view the
+ * flag is cleared and normal event dispatch is performed. This ensures that the platform can
+ * click on any view that has accessibility focus which is semantically equivalent to asking the
+ * view to perform a click accessibility action but more generic as views not implementing click
+ * action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ TARGET_ACCESSIBILITY_FOCUS = 0x40000000,
+}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index a2192cb..60fb00e 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -16,13 +16,13 @@
}
flag {
- name: "enable_gestures_library_timer_provider"
+ name: "remove_input_channel_from_windowstate"
namespace: "input"
- description: "Set to true to enable timer support for the touchpad Gestures library"
- bug: "297192727"
- }
+ description: "Do not store a copy of input channel inside WindowState."
+ bug: "323450804"
+}
- flag {
+flag {
name: "enable_input_event_tracing"
namespace: "input"
description: "Set to true to enable input event tracing, including always-on tracing on non-user builds"
@@ -37,6 +37,13 @@
}
flag {
+ name: "split_all_touches"
+ namespace: "input"
+ description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality"
+ bug: "239934827"
+}
+
+flag {
name: "a11y_crash_on_inconsistent_event_stream"
namespace: "accessibility"
description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
@@ -87,13 +94,6 @@
}
flag {
- name: "remove_pointer_event_tracking_in_wm"
- namespace: "input"
- description: "Remove pointer event tracking in WM after the Pointer Icon Refactor"
- bug: "315321016"
-}
-
-flag {
name: "enable_new_mouse_pointer_ballistics"
namespace: "input"
description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones"
@@ -157,3 +157,53 @@
description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
bug: "263559234"
}
+
+flag {
+ name: "show_pointers_for_partial_screenshare"
+ namespace: "input"
+ description: "Show touch and pointer indicators when mirroring a single task"
+ bug: "310179437"
+}
+
+flag {
+ name: "include_relative_axis_values_for_captured_touchpads"
+ namespace: "input"
+ description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads."
+ bug: "330522990"
+}
+
+flag {
+ name: "enable_per_device_input_latency_metrics"
+ namespace: "input"
+ description: "Capture input latency metrics on a per device granular level using histograms."
+ bug: "270049345"
+}
+
+flag {
+ name: "collect_palm_rejection_quality_metrics"
+ namespace: "input"
+ description: "Collect quality metrics on framework palm rejection."
+ bug: "341717757"
+}
+
+flag {
+ name: "enable_touchpad_no_focus_change"
+ namespace: "input"
+ description: "Prevents touchpad gesture changing window focus."
+ bug: "364460018"
+}
+
+flag {
+ name: "enable_input_policy_profile"
+ namespace: "input"
+ description: "Apply input policy profile for input threads."
+ bug: "347122505"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "keyboard_repeat_keys"
+ namespace: "input"
+ description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates."
+ bug: "336585002"
+}
diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp
index 018d199..63853f7 100644
--- a/libs/input/rust/Android.bp
+++ b/libs/input/rust/Android.bp
@@ -24,6 +24,8 @@
"liblogger",
"liblog_rust",
"inputconstants-rust",
+ "libserde",
+ "libserde_json",
],
whole_static_libs: [
"libinput_from_rust_to_cpp",
diff --git a/libs/input/rust/data_store.rs b/libs/input/rust/data_store.rs
new file mode 100644
index 0000000..6bdcefd
--- /dev/null
+++ b/libs/input/rust/data_store.rs
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+//! Contains the DataStore, used to store input related data in a persistent way.
+
+use crate::input::KeyboardType;
+use log::{debug, error};
+use serde::{Deserialize, Serialize};
+use std::fs::File;
+use std::io::{Read, Write};
+use std::path::Path;
+use std::sync::{Arc, RwLock};
+
+/// Data store to be used to store information that persistent across device reboots.
+pub struct DataStore {
+ file_reader_writer: Box<dyn FileReaderWriter>,
+ inner: Arc<RwLock<DataStoreInner>>,
+}
+
+#[derive(Default)]
+struct DataStoreInner {
+ is_loaded: bool,
+ data: Data,
+}
+
+#[derive(Default, Serialize, Deserialize)]
+struct Data {
+ // Map storing data for keyboard classification for specific devices.
+ #[serde(default)]
+ keyboard_classifications: Vec<KeyboardClassification>,
+ // NOTE: Important things to consider:
+ // - Add any data that needs to be persisted here in this struct.
+ // - Mark all new fields with "#[serde(default)]" for backward compatibility.
+ // - Also, you can't modify the already added fields.
+ // - Can add new nested fields to existing structs. e.g. Add another field to the struct
+ // KeyboardClassification and mark it "#[serde(default)]".
+}
+
+#[derive(Default, Serialize, Deserialize)]
+struct KeyboardClassification {
+ descriptor: String,
+ keyboard_type: KeyboardType,
+ is_finalized: bool,
+}
+
+impl DataStore {
+ /// Creates a new instance of Data store
+ pub fn new(file_reader_writer: Box<dyn FileReaderWriter>) -> Self {
+ Self { file_reader_writer, inner: Default::default() }
+ }
+
+ fn load(&mut self) {
+ if self.inner.read().unwrap().is_loaded {
+ return;
+ }
+ self.load_internal();
+ }
+
+ fn load_internal(&mut self) {
+ let s = self.file_reader_writer.read();
+ let data: Data = if !s.is_empty() {
+ let deserialize: Data = match serde_json::from_str(&s) {
+ Ok(deserialize) => deserialize,
+ Err(msg) => {
+ error!("Unable to deserialize JSON data into struct: {:?} -> {:?}", msg, s);
+ Default::default()
+ }
+ };
+ deserialize
+ } else {
+ Default::default()
+ };
+
+ let mut inner = self.inner.write().unwrap();
+ inner.data = data;
+ inner.is_loaded = true;
+ }
+
+ fn save(&mut self) {
+ let string_to_save;
+ {
+ let inner = self.inner.read().unwrap();
+ string_to_save = serde_json::to_string(&inner.data).unwrap();
+ }
+ self.file_reader_writer.write(string_to_save);
+ }
+
+ /// Get keyboard type of the device (as stored in the data store)
+ pub fn get_keyboard_type(&mut self, descriptor: &String) -> Option<(KeyboardType, bool)> {
+ self.load();
+ let data = &self.inner.read().unwrap().data;
+ for keyboard_classification in data.keyboard_classifications.iter() {
+ if keyboard_classification.descriptor == *descriptor {
+ return Some((
+ keyboard_classification.keyboard_type,
+ keyboard_classification.is_finalized,
+ ));
+ }
+ }
+ None
+ }
+
+ /// Save keyboard type of the device in the data store
+ pub fn set_keyboard_type(
+ &mut self,
+ descriptor: &String,
+ keyboard_type: KeyboardType,
+ is_finalized: bool,
+ ) {
+ {
+ let data = &mut self.inner.write().unwrap().data;
+ data.keyboard_classifications
+ .retain(|classification| classification.descriptor != *descriptor);
+ data.keyboard_classifications.push(KeyboardClassification {
+ descriptor: descriptor.to_string(),
+ keyboard_type,
+ is_finalized,
+ })
+ }
+ self.save();
+ }
+}
+
+pub trait FileReaderWriter {
+ fn read(&self) -> String;
+ fn write(&self, to_write: String);
+}
+
+/// Default file reader writer implementation
+pub struct DefaultFileReaderWriter {
+ filepath: String,
+}
+
+impl DefaultFileReaderWriter {
+ /// Creates a new instance of Default file reader writer that can read and write string to a
+ /// particular file in the filesystem
+ pub fn new(filepath: String) -> Self {
+ Self { filepath }
+ }
+}
+
+impl FileReaderWriter for DefaultFileReaderWriter {
+ fn read(&self) -> String {
+ let path = Path::new(&self.filepath);
+ let mut fs_string = String::new();
+ match File::open(path) {
+ Err(e) => error!("couldn't open {:?}: {}", path, e),
+ Ok(mut file) => match file.read_to_string(&mut fs_string) {
+ Err(e) => error!("Couldn't read from {:?}: {}", path, e),
+ Ok(_) => debug!("Successfully read from file {:?}", path),
+ },
+ };
+ fs_string
+ }
+
+ fn write(&self, to_write: String) {
+ let path = Path::new(&self.filepath);
+ match File::create(path) {
+ Err(e) => error!("couldn't create {:?}: {}", path, e),
+ Ok(mut file) => match file.write_all(to_write.as_bytes()) {
+ Err(e) => error!("Couldn't write to {:?}: {}", path, e),
+ Ok(_) => debug!("Successfully saved to file {:?}", path),
+ },
+ };
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::data_store::{
+ test_file_reader_writer::TestFileReaderWriter, DataStore, FileReaderWriter,
+ };
+ use crate::input::KeyboardType;
+
+ #[test]
+ fn test_backward_compatibility_version_1() {
+ // This test tests JSON string that will be created by the first version of data store
+ // This test SHOULD NOT be modified
+ let test_reader_writer = TestFileReaderWriter::new();
+ test_reader_writer.write(r#"{"keyboard_classifications":[{"descriptor":"descriptor","keyboard_type":{"type":"Alphabetic"},"is_finalized":true}]}"#.to_string());
+
+ let mut data_store = DataStore::new(Box::new(test_reader_writer));
+ let (keyboard_type, is_finalized) =
+ data_store.get_keyboard_type(&"descriptor".to_string()).unwrap();
+ assert_eq!(keyboard_type, KeyboardType::Alphabetic);
+ assert!(is_finalized);
+ }
+}
+
+#[cfg(test)]
+pub mod test_file_reader_writer {
+
+ use crate::data_store::FileReaderWriter;
+ use std::sync::{Arc, RwLock};
+
+ #[derive(Default)]
+ struct TestFileReaderWriterInner {
+ fs_string: String,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestFileReaderWriter(Arc<RwLock<TestFileReaderWriterInner>>);
+
+ impl TestFileReaderWriter {
+ pub fn new() -> Self {
+ Default::default()
+ }
+ }
+
+ impl FileReaderWriter for TestFileReaderWriter {
+ fn read(&self) -> String {
+ self.0.read().unwrap().fs_string.clone()
+ }
+
+ fn write(&self, fs_string: String) {
+ self.0.write().unwrap().fs_string = fs_string;
+ }
+ }
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 564d94d..90f509d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -19,6 +19,8 @@
use crate::ffi::RustInputDeviceIdentifier;
use bitflags::bitflags;
use inputconstants::aidl::android::os::IInputConstants;
+use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag;
+use serde::{Deserialize, Serialize};
use std::fmt;
/// The InputDevice ID.
@@ -193,31 +195,34 @@
bitflags! {
/// MotionEvent flags.
+ /// The source of truth for the flag definitions are the MotionEventFlag AIDL enum.
+ /// The flag values are redefined here as a bitflags API.
#[derive(Debug)]
pub struct MotionFlags: u32 {
/// FLAG_WINDOW_IS_OBSCURED
- const WINDOW_IS_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32;
+ const WINDOW_IS_OBSCURED = MotionEventFlag::WINDOW_IS_OBSCURED.0 as u32;
/// FLAG_WINDOW_IS_PARTIALLY_OBSCURED
- const WINDOW_IS_PARTIALLY_OBSCURED = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32;
+ const WINDOW_IS_PARTIALLY_OBSCURED = MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED.0 as u32;
/// FLAG_HOVER_EXIT_PENDING
- const HOVER_EXIT_PENDING = IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32;
+ const HOVER_EXIT_PENDING = MotionEventFlag::HOVER_EXIT_PENDING.0 as u32;
/// FLAG_IS_GENERATED_GESTURE
- const IS_GENERATED_GESTURE = IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32;
+ const IS_GENERATED_GESTURE = MotionEventFlag::IS_GENERATED_GESTURE.0 as u32;
/// FLAG_CANCELED
- const CANCELED = IInputConstants::INPUT_EVENT_FLAG_CANCELED as u32;
+ const CANCELED = MotionEventFlag::CANCELED.0 as u32;
/// FLAG_NO_FOCUS_CHANGE
- const NO_FOCUS_CHANGE = IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32;
+ const NO_FOCUS_CHANGE = MotionEventFlag::NO_FOCUS_CHANGE.0 as u32;
/// PRIVATE_FLAG_SUPPORTS_ORIENTATION
- const PRIVATE_SUPPORTS_ORIENTATION = IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION as u32;
+ const PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+ MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION.0 as u32;
/// PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION
- const PRIVATE_SUPPORTS_DIRECTIONAL_ORIENTATION =
- IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION as u32;
+ const PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION =
+ MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION.0 as u32;
/// FLAG_IS_ACCESSIBILITY_EVENT
- const IS_ACCESSIBILITY_EVENT = IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32;
+ const IS_ACCESSIBILITY_EVENT = MotionEventFlag::IS_ACCESSIBILITY_EVENT.0 as u32;
/// FLAG_TAINTED
- const TAINTED = IInputConstants::INPUT_EVENT_FLAG_TAINTED as u32;
+ const TAINTED = MotionEventFlag::TAINTED.0 as u32;
/// FLAG_TARGET_ACCESSIBILITY_FOCUS
- const TARGET_ACCESSIBILITY_FOCUS = IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32;
+ const TARGET_ACCESSIBILITY_FOCUS = MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS.0 as u32;
}
}
@@ -320,9 +325,11 @@
/// A rust enum representation of a Keyboard type.
#[repr(u32)]
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
pub enum KeyboardType {
/// KEYBOARD_TYPE_NONE
+ #[default]
None = input_bindgen::AINPUT_KEYBOARD_TYPE_NONE,
/// KEYBOARD_TYPE_NON_ALPHABETIC
NonAlphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
@@ -333,10 +340,24 @@
#[cfg(test)]
mod tests {
use crate::input::SourceClass;
+ use crate::MotionFlags;
use crate::Source;
+ use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag;
+
#[test]
fn convert_source_class_pointer() {
let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap();
assert!(source.is_from_class(SourceClass::Pointer));
}
+
+ /// Ensure all MotionEventFlag constants are re-defined in rust.
+ #[test]
+ fn all_motion_event_flags_defined() {
+ for flag in MotionEventFlag::enum_values() {
+ assert!(
+ MotionFlags::from_bits(flag.0 as u32).is_some(),
+ "MotionEventFlag value {flag:?} is not redefined as a rust MotionFlags"
+ );
+ }
+ }
}
diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs
new file mode 100644
index 0000000..ab74efb
--- /dev/null
+++ b/libs/input/rust/keyboard_classification_config.rs
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+use crate::input::KeyboardType;
+
+// TODO(b/263559234): Categorize some of these to KeyboardType::None based on ability to produce
+// key events at all. (Requires setup allowing InputDevice to dynamically add/remove
+// KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends
+// up producing a key event)
+pub static CLASSIFIED_DEVICES: &[(
+ /* vendorId */ u16,
+ /* productId */ u16,
+ KeyboardType,
+ /* is_finalized */ bool,
+)] = &[
+ // HP X4000 Wireless Mouse
+ (0x03f0, 0xa407, KeyboardType::NonAlphabetic, true),
+ // Microsoft Wireless Mobile Mouse 6000
+ (0x045e, 0x0745, KeyboardType::NonAlphabetic, true),
+ // Microsoft Surface Precision Mouse
+ (0x045e, 0x0821, KeyboardType::NonAlphabetic, true),
+ // Microsoft Pro IntelliMouse
+ (0x045e, 0x082a, KeyboardType::NonAlphabetic, true),
+ // Microsoft Bluetooth Mouse
+ (0x045e, 0x082f, KeyboardType::NonAlphabetic, true),
+ // Xbox One Elite Series 2 gamepad
+ (0x045e, 0x0b05, KeyboardType::NonAlphabetic, true),
+ // Logitech T400
+ (0x046d, 0x4026, KeyboardType::NonAlphabetic, true),
+ // Logitech M720 Triathlon (Unifying)
+ (0x046d, 0x405e, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master 2S (Unifying)
+ (0x046d, 0x4069, KeyboardType::NonAlphabetic, true),
+ // Logitech M585 (Unifying)
+ (0x046d, 0x406b, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2 (Unifying)
+ (0x046d, 0x4072, KeyboardType::NonAlphabetic, true),
+ // Logitech Pebble M350
+ (0x046d, 0x4080, KeyboardType::NonAlphabetic, true),
+ // Logitech T630 Ultrathin
+ (0x046d, 0xb00d, KeyboardType::NonAlphabetic, true),
+ // Logitech M558
+ (0x046d, 0xb011, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master (Bluetooth)
+ (0x046d, 0xb012, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2 (Bluetooth)
+ (0x046d, 0xb013, KeyboardType::NonAlphabetic, true),
+ // Logitech M720 Triathlon (Bluetooth)
+ (0x046d, 0xb015, KeyboardType::NonAlphabetic, true),
+ // Logitech M535
+ (0x046d, 0xb016, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master / Anywhere 2 (Bluetooth)
+ (0x046d, 0xb017, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master 2S (Bluetooth)
+ (0x046d, 0xb019, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2S (Bluetooth)
+ (0x046d, 0xb01a, KeyboardType::NonAlphabetic, true),
+ // Logitech M585/M590 (Bluetooth)
+ (0x046d, 0xb01b, KeyboardType::NonAlphabetic, true),
+ // Logitech G603 Lightspeed Gaming Mouse (Bluetooth)
+ (0x046d, 0xb01c, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master (Bluetooth)
+ (0x046d, 0xb01e, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Anywhere 2 (Bluetooth)
+ (0x046d, 0xb01f, KeyboardType::NonAlphabetic, true),
+ // Logitech MX Master 3 (Bluetooth)
+ (0x046d, 0xb023, KeyboardType::NonAlphabetic, true),
+ // Logitech G604 Lightspeed Gaming Mouse (Bluetooth)
+ (0x046d, 0xb024, KeyboardType::NonAlphabetic, true),
+ // Logitech Spotlight Presentation Remote (Bluetooth)
+ (0x046d, 0xb503, KeyboardType::NonAlphabetic, true),
+ // Logitech R500 (Bluetooth)
+ (0x046d, 0xb505, KeyboardType::NonAlphabetic, true),
+ // Logitech M500s
+ (0x046d, 0xc093, KeyboardType::NonAlphabetic, true),
+ // Logitech Spotlight Presentation Remote (USB dongle)
+ (0x046d, 0xc53e, KeyboardType::NonAlphabetic, true),
+ // Elecom Enelo IR LED Mouse 350
+ (0x056e, 0x0134, KeyboardType::NonAlphabetic, true),
+ // Elecom EPRIM Blue LED 5 button mouse 228
+ (0x056e, 0x0141, KeyboardType::NonAlphabetic, true),
+ // Elecom Blue LED Mouse 203
+ (0x056e, 0x0159, KeyboardType::NonAlphabetic, true),
+ // Zebra LS2208 barcode scanner
+ (0x05e0, 0x1200, KeyboardType::NonAlphabetic, true),
+ // RDing FootSwitch1F1
+ (0x0c45, 0x7403, KeyboardType::NonAlphabetic, true),
+ // SteelSeries Sensei RAW Frost Blue
+ (0x1038, 0x1369, KeyboardType::NonAlphabetic, true),
+ // SteelSeries Rival 3 Wired
+ (0x1038, 0x1824, KeyboardType::NonAlphabetic, true),
+ // SteelSeries Rival 3 Wireless (USB dongle)
+ (0x1038, 0x1830, KeyboardType::NonAlphabetic, true),
+ // Yubico.com Yubikey
+ (0x1050, 0x0010, KeyboardType::NonAlphabetic, true),
+ // Yubico.com Yubikey 4 OTP+U2F+CCID
+ (0x1050, 0x0407, KeyboardType::NonAlphabetic, true),
+ // Lenovo USB-C Wired Compact Mouse
+ (0x17ef, 0x6123, KeyboardType::NonAlphabetic, true),
+ // Corsair Katar Pro Wireless (USB dongle)
+ (0x1b1c, 0x1b94, KeyboardType::NonAlphabetic, true),
+ // Corsair Katar Pro Wireless (Bluetooth)
+ (0x1bae, 0x1b1c, KeyboardType::NonAlphabetic, true),
+ // Kensington Pro Fit Full-size
+ (0x1bcf, 0x08a0, KeyboardType::NonAlphabetic, true),
+ // Huion HS64
+ (0x256c, 0x006d, KeyboardType::NonAlphabetic, true),
+ // XP-Pen Star G640
+ (0x28bd, 0x0914, KeyboardType::NonAlphabetic, true),
+ // XP-Pen Artist 12 Pro
+ (0x28bd, 0x091f, KeyboardType::NonAlphabetic, true),
+ // XP-Pen Deco mini7W
+ (0x28bd, 0x0928, KeyboardType::NonAlphabetic, true),
+];
diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs
index 1063fac..3c789b4 100644
--- a/libs/input/rust/keyboard_classifier.rs
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -31,39 +31,37 @@
//! across multiple device connections in a time period, then change type to
//! KeyboardType::NonAlphabetic. Once changed, it can still change back to Alphabetic
//! (i.e. verified = false).
-//!
-//! TODO(b/263559234): Data store implementation to store information about past classification
+use crate::data_store::DataStore;
use crate::input::{DeviceId, InputDevice, KeyboardType};
+use crate::keyboard_classification_config::CLASSIFIED_DEVICES;
use crate::{DeviceClass, ModifierState};
use std::collections::HashMap;
/// The KeyboardClassifier is used to classify a keyboard device into non-keyboard, alphabetic
/// keyboard or non-alphabetic keyboard
-#[derive(Default)]
pub struct KeyboardClassifier {
device_map: HashMap<DeviceId, KeyboardInfo>,
+ data_store: DataStore,
}
struct KeyboardInfo {
- _device: InputDevice,
+ device: InputDevice,
keyboard_type: KeyboardType,
is_finalized: bool,
}
impl KeyboardClassifier {
/// Create a new KeyboardClassifier
- pub fn new() -> Self {
- Default::default()
+ pub fn new(data_store: DataStore) -> Self {
+ Self { device_map: HashMap::new(), data_store }
}
/// Adds keyboard to KeyboardClassifier
pub fn notify_keyboard_changed(&mut self, device: InputDevice) {
let (keyboard_type, is_finalized) = self.classify_keyboard(&device);
- self.device_map.insert(
- device.device_id,
- KeyboardInfo { _device: device, keyboard_type, is_finalized },
- );
+ self.device_map
+ .insert(device.device_id, KeyboardInfo { device, keyboard_type, is_finalized });
}
/// Get keyboard type for a tracked keyboard in KeyboardClassifier
@@ -106,11 +104,16 @@
if Self::is_alphabetic_key(&evdev_code) {
keyboard.keyboard_type = KeyboardType::Alphabetic;
keyboard.is_finalized = true;
+ self.data_store.set_keyboard_type(
+ &keyboard.device.identifier.descriptor,
+ keyboard.keyboard_type,
+ keyboard.is_finalized,
+ );
}
}
}
- fn classify_keyboard(&self, device: &InputDevice) -> (KeyboardType, bool) {
+ fn classify_keyboard(&mut self, device: &InputDevice) -> (KeyboardType, bool) {
// This should never happen but having keyboard device class is necessary to be classified
// as any type of keyboard.
if !device.classes.contains(DeviceClass::Keyboard) {
@@ -126,6 +129,21 @@
(KeyboardType::NonAlphabetic, true)
};
}
+
+ // Check in data store
+ if let Some((keyboard_type, is_finalized)) =
+ self.data_store.get_keyboard_type(&device.identifier.descriptor)
+ {
+ return (keyboard_type, is_finalized);
+ }
+
+ // Check in known device list for classification
+ for (vendor, product, keyboard_type, is_finalized) in CLASSIFIED_DEVICES.iter() {
+ if device.identifier.vendor == *vendor && device.identifier.product == *product {
+ return (*keyboard_type, *is_finalized);
+ }
+ }
+
// Any composite device with multiple device classes should be categorized as non-alphabetic
// keyboard initially
if device.classes.contains(DeviceClass::Touch)
@@ -168,17 +186,20 @@
#[cfg(test)]
mod tests {
+ use crate::data_store::{test_file_reader_writer::TestFileReaderWriter, DataStore};
use crate::input::{DeviceId, InputDevice, KeyboardType};
+ use crate::keyboard_classification_config::CLASSIFIED_DEVICES;
use crate::keyboard_classifier::KeyboardClassifier;
use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier};
static DEVICE_ID: DeviceId = DeviceId(1);
+ static SECOND_DEVICE_ID: DeviceId = DeviceId(2);
static KEY_A: i32 = 30;
static KEY_1: i32 = 2;
#[test]
fn classify_external_alphabetic_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
));
@@ -188,7 +209,7 @@
#[test]
fn classify_external_non_alphabetic_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier
.notify_keyboard_changed(create_device(DeviceClass::Keyboard | DeviceClass::External));
assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
@@ -197,7 +218,7 @@
#[test]
fn classify_mouse_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Cursor
@@ -210,7 +231,7 @@
#[test]
fn classify_touchpad_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Touchpad
@@ -223,7 +244,7 @@
#[test]
fn classify_stylus_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::ExternalStylus
@@ -236,7 +257,7 @@
#[test]
fn classify_dpad_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -249,7 +270,7 @@
#[test]
fn classify_joystick_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Joystick
@@ -262,7 +283,7 @@
#[test]
fn classify_gamepad_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Gamepad
@@ -275,7 +296,7 @@
#[test]
fn reclassify_keyboard_on_alphabetic_key_event() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -293,7 +314,7 @@
#[test]
fn dont_reclassify_keyboard_on_non_alphabetic_key_event() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -311,7 +332,7 @@
#[test]
fn dont_reclassify_keyboard_on_alphabetic_key_event_with_modifiers() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -326,20 +347,82 @@
assert!(!classifier.is_finalized(DEVICE_ID));
}
+ #[test]
+ fn classify_known_devices() {
+ let mut classifier = create_classifier();
+ for (vendor, product, keyboard_type, is_finalized) in CLASSIFIED_DEVICES.iter() {
+ classifier
+ .notify_keyboard_changed(create_device_with_vendor_product_ids(*vendor, *product));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), *keyboard_type);
+ assert_eq!(classifier.is_finalized(DEVICE_ID), *is_finalized);
+ }
+ }
+
+ #[test]
+ fn classify_previously_reclassified_devices() {
+ let test_reader_writer = TestFileReaderWriter::new();
+ {
+ let mut classifier =
+ KeyboardClassifier::new(DataStore::new(Box::new(test_reader_writer.clone())));
+ let device = create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ );
+ classifier.notify_keyboard_changed(device);
+ classifier.process_key(DEVICE_ID, KEY_A, ModifierState::None);
+ }
+
+ // Re-create classifier and data store to mimic a reboot (but use the same file system
+ // reader writer)
+ {
+ let mut classifier =
+ KeyboardClassifier::new(DataStore::new(Box::new(test_reader_writer.clone())));
+ let device = InputDevice {
+ device_id: SECOND_DEVICE_ID,
+ identifier: create_identifier(/* vendor= */ 234, /* product= */ 345),
+ classes: DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ };
+ classifier.notify_keyboard_changed(device);
+ assert_eq!(classifier.get_keyboard_type(SECOND_DEVICE_ID), KeyboardType::Alphabetic);
+ assert!(classifier.is_finalized(SECOND_DEVICE_ID));
+ }
+ }
+
+ fn create_classifier() -> KeyboardClassifier {
+ KeyboardClassifier::new(DataStore::new(Box::new(TestFileReaderWriter::new())))
+ }
+
+ fn create_identifier(vendor: u16, product: u16) -> RustInputDeviceIdentifier {
+ RustInputDeviceIdentifier {
+ name: "test_device".to_string(),
+ location: "location".to_string(),
+ unique_id: "unique_id".to_string(),
+ bus: 123,
+ vendor,
+ product,
+ version: 567,
+ descriptor: "descriptor".to_string(),
+ }
+ }
+
fn create_device(classes: DeviceClass) -> InputDevice {
InputDevice {
device_id: DEVICE_ID,
- identifier: RustInputDeviceIdentifier {
- name: "test_device".to_string(),
- location: "location".to_string(),
- unique_id: "unique_id".to_string(),
- bus: 123,
- vendor: 234,
- product: 345,
- version: 567,
- descriptor: "descriptor".to_string(),
- },
+ identifier: create_identifier(/* vendor= */ 234, /* product= */ 345),
classes,
}
}
+
+ fn create_device_with_vendor_product_ids(vendor: u16, product: u16) -> InputDevice {
+ InputDevice {
+ device_id: DEVICE_ID,
+ identifier: create_identifier(vendor, product),
+ classes: DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
+ }
+ }
}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 8837469..4f4ea85 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -16,12 +16,16 @@
//! The rust component of libinput.
+mod data_store;
mod input;
mod input_verifier;
+mod keyboard_classification_config;
mod keyboard_classifier;
+pub use data_store::{DataStore, DefaultFileReaderWriter};
pub use input::{
- DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source,
+ DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionFlags,
+ Source,
};
pub use input_verifier::InputVerifier;
pub use keyboard_classifier::KeyboardClassifier;
@@ -149,7 +153,14 @@
}
fn create_keyboard_classifier() -> Box<KeyboardClassifier> {
- Box::new(KeyboardClassifier::new())
+ // Future design: Make this data store singleton by passing it to C++ side and making it global
+ // and pass by reference to components that need to store persistent data.
+ //
+ // Currently only used by rust keyboard classifier so keeping it here.
+ let data_store = DataStore::new(Box::new(DefaultFileReaderWriter::new(
+ "/data/system/inputflinger-data.json".to_string(),
+ )));
+ Box::new(KeyboardClassifier::new(data_store))
}
fn notify_keyboard_changed(
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 9c0a41e..81c6175 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -16,6 +16,7 @@
"BlockingQueue_test.cpp",
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
+ "InputConsumer_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
@@ -23,7 +24,9 @@
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
+ "Resampler_test.cpp",
"RingBuffer_test.cpp",
+ "TestInputChannel.cpp",
"TfLiteMotionPredictor_test.cpp",
"TouchResampling_test.cpp",
"TouchVideoFrame_test.cpp",
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 435bdcd..25356cf 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -65,11 +65,7 @@
ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
- // Name
- EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str())
- << "server channel should have suffixed name";
- EXPECT_STREQ("channel name (client)", clientChannel->getName().c_str())
- << "client channel should have suffixed name";
+ EXPECT_EQ(serverChannel->getName(), clientChannel->getName());
// Server->Client communication
InputMessage serverMsg = {};
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
new file mode 100644
index 0000000..d708316
--- /dev/null
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -0,0 +1,247 @@
+/**
+ * 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.
+ */
+
+#include <input/InputConsumerNoResampling.h>
+
+#include <memory>
+#include <optional>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <android-base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/BlockingQueue.h>
+#include <input/InputEventBuilders.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace {
+
+using std::chrono::nanoseconds;
+
+using ::testing::AllOf;
+using ::testing::Matcher;
+using ::testing::Not;
+
+} // namespace
+
+class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
+protected:
+ InputConsumerTest()
+ : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
+ mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+ Looper::setForThread(mLooper);
+ mConsumer =
+ std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
+ std::make_unique<LegacyResampler>());
+ }
+
+ void invokeLooperCallback() const {
+ sp<LooperCallback> callback;
+ ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr));
+ callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ }
+
+ void assertOnBatchedInputEventPendingWasCalled() {
+ ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
+ << "onBatchedInputEventPending has not been called.";
+ --mOnBatchedInputEventPendingInvocationCount;
+ }
+
+ void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+ std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
+ ASSERT_NE(motionEvent, nullptr);
+ EXPECT_THAT(*motionEvent, matcher);
+ }
+
+ std::shared_ptr<TestInputChannel> mClientTestChannel;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+ BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
+ BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+private:
+ size_t mOnBatchedInputEventPendingInvocationCount{0};
+
+ // InputConsumerCallbacks interface
+ void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+ mKeyEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+ if (!mConsumer->probablyHasInput()) {
+ ADD_FAILURE() << "should deterministically have input because there is a batch";
+ }
+ ++mOnBatchedInputEventPendingInvocationCount;
+ };
+ void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+ mFocusEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ };
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ };
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ };
+};
+
+TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .eventTime(nanoseconds{0ms}.count())
+ .action(AMOTION_EVENT_ACTION_DOWN)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .eventTime(nanoseconds{5ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .eventTime(nanoseconds{10ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+
+ assertOnBatchedInputEventPendingWasCalled();
+
+ mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
+
+ std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(downMotionEvent, nullptr);
+
+ std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(moveMotionEvent, nullptr);
+ EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+}
+
+TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .eventTime(nanoseconds{0ms}.count())
+ .action(AMOTION_EVENT_ACTION_DOWN)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .eventTime(nanoseconds{5ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .eventTime(nanoseconds{10ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
+ .eventTime(nanoseconds{15ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+
+ assertOnBatchedInputEventPendingWasCalled();
+
+ mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
+
+ std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(downMotionEvent, nullptr);
+
+ std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(moveMotionEvent, nullptr);
+ const size_t numSamples = moveMotionEvent->getHistorySize() + 1;
+ EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2),
+ moveMotionEvent->getEventTime());
+
+ // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns
+ // consumer is set to null before destroying consumer. This leads to a member function call on a
+ // null object.
+ // TODO(b/332613662): Remove this workaround.
+ mConsumer->consumeBatchedInputEvents(std::nullopt);
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
+}
+
+TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) {
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .deviceId(0)
+ .action(AMOTION_EVENT_ACTION_DOWN)
+ .build());
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .deviceId(0)
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .deviceId(0)
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
+ .deviceId(0)
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4}
+ .deviceId(1)
+ .action(AMOTION_EVENT_ACTION_DOWN)
+ .build());
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5}
+ .deviceId(0)
+ .action(AMOTION_EVENT_ACTION_UP)
+ .build());
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ Not(MotionEventIsResampled())));
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+} // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 3717f49..a67e1ef 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -371,8 +371,8 @@
mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME,
ARBITRARY_EVENT_TIME, 2, mPointerProperties, mSamples[0].pointerCoords);
- event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
- event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords, event->getId());
+ event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords, event->getId());
}
void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
@@ -591,6 +591,22 @@
ASSERT_EQ(event.getX(0), copy.getX(0));
}
+TEST_F(MotionEventTest, CheckEventIdWithHistoryIsIncremented) {
+ MotionEvent event;
+ constexpr int32_t ARBITRARY_ID = 42;
+ event.initialize(ARBITRARY_ID, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
+ AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
+ mPointerProperties, mSamples[0].pointerCoords);
+ ASSERT_EQ(event.getId(), ARBITRARY_ID);
+ event.addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords, ARBITRARY_ID + 1);
+ ASSERT_EQ(event.getId(), ARBITRARY_ID + 1);
+ event.addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords, ARBITRARY_ID + 2);
+ ASSERT_EQ(event.getId(), ARBITRARY_ID + 2);
+}
+
TEST_F(MotionEventTest, SplitPointerDown) {
MotionEvent event = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.downTime(ARBITRARY_DOWN_TIME)
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index f49469c..1210f71 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -52,7 +52,7 @@
const int32_t action;
const nsecs_t downTime;
const uint32_t seq;
- const int32_t eventId;
+ int32_t eventId;
const int32_t deviceId = 1;
const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT;
@@ -291,6 +291,7 @@
void publishAndConsumeKeyEvent();
void publishAndConsumeMotionStream();
void publishAndConsumeMotionDown(nsecs_t downTime);
+ void publishAndConsumeSinglePointerMultipleSamples(const size_t nSamples);
void publishAndConsumeBatchedMotionMove(nsecs_t downTime);
void publishAndConsumeFocusEvent();
void publishAndConsumeCaptureEvent();
@@ -298,6 +299,7 @@
void publishAndConsumeTouchModeEvent();
void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime,
const std::vector<Pointer>& pointers);
+
void TearDown() override {
// Destroy the consumer, flushing any of the pending ack's.
sendMessage(LooperMessage::DESTROY_CONSUMER);
@@ -362,7 +364,7 @@
if (!mConsumer->probablyHasInput()) {
ADD_FAILURE() << "should deterministically have input because there is a batch";
}
- mConsumer->consumeBatchedInputEvents(std::nullopt);
+ mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
};
void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
mFocusEvents.push(std::move(event));
@@ -394,8 +396,9 @@
break;
}
case LooperMessage::CREATE_CONSUMER: {
- mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel),
- mLooper, *this);
+ mConsumer =
+ std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), mLooper,
+ *this, /*resampler=*/nullptr);
break;
}
case LooperMessage::DESTROY_CONSUMER: {
@@ -519,6 +522,123 @@
{Pointer{.id = 0, .x = 20, .y = 30}});
}
+/*
+ * Decompose a potential multi-sampled MotionEvent into multiple MotionEvents
+ * with a single sample.
+ */
+std::vector<MotionEvent> splitBatchedMotionEvent(const MotionEvent& batchedMotionEvent) {
+ std::vector<MotionEvent> singleMotionEvents;
+ const size_t batchSize = batchedMotionEvent.getHistorySize() + 1;
+ for (size_t i = 0; i < batchSize; ++i) {
+ MotionEvent singleMotionEvent;
+ singleMotionEvent
+ .initialize(batchedMotionEvent.getId(), batchedMotionEvent.getDeviceId(),
+ batchedMotionEvent.getSource(), batchedMotionEvent.getDisplayId(),
+ batchedMotionEvent.getHmac(), batchedMotionEvent.getAction(),
+ batchedMotionEvent.getActionButton(), batchedMotionEvent.getFlags(),
+ batchedMotionEvent.getEdgeFlags(), batchedMotionEvent.getMetaState(),
+ batchedMotionEvent.getButtonState(),
+ batchedMotionEvent.getClassification(),
+ batchedMotionEvent.getTransform(), batchedMotionEvent.getXPrecision(),
+ batchedMotionEvent.getYPrecision(),
+ batchedMotionEvent.getRawXCursorPosition(),
+ batchedMotionEvent.getRawYCursorPosition(),
+ batchedMotionEvent.getRawTransform(), batchedMotionEvent.getDownTime(),
+ batchedMotionEvent.getHistoricalEventTime(/*historicalIndex=*/i),
+ batchedMotionEvent.getPointerCount(),
+ batchedMotionEvent.getPointerProperties(),
+ (batchedMotionEvent.getSamplePointerCoords() + i));
+ singleMotionEvents.push_back(singleMotionEvent);
+ }
+ return singleMotionEvents;
+}
+
+/*
+ * Simulates a single pointer touching the screen and leaving it there for a period of time.
+ * Publishes a DOWN event and consumes it right away. Then, publishes a sequence of MOVE
+ * samples for the same pointer, and waits until it has been consumed. Splits batched MotionEvents
+ * into individual samples. Checks the consumed MotionEvents against the published ones.
+ * This test is non-deterministic because it depends on the timing of arrival of events to the
+ * socket.
+ *
+ * @param nSamples The number of MOVE samples to publish before attempting consumption.
+ */
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMultipleSamples(
+ const size_t nSamples) {
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const Pointer pointer(0, 20, 30);
+
+ const PublishMotionArgs argsDown(AMOTION_EVENT_ACTION_DOWN, downTime, {pointer}, mSeq);
+ const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC);
+ publishMotionEvent(*mPublisher, argsDown);
+
+ // Consume the DOWN event.
+ ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value());
+
+ verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown);
+
+ std::vector<nsecs_t> publishTimes;
+ std::vector<PublishMotionArgs> argsMoves;
+ std::queue<uint32_t> publishedSequenceNumbers;
+
+ // Block Looper to increase the chance of batching events
+ {
+ std::scoped_lock l(mLock);
+ mLooperMayProceed = false;
+ }
+ sendMessage(LooperMessage::BLOCK_LOOPER);
+ {
+ std::unique_lock l(mLock);
+ mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
+ }
+
+ uint32_t firstSampleId;
+ for (size_t i = 0; i < nSamples; ++i) {
+ publishedSequenceNumbers.push(++mSeq);
+ PublishMotionArgs argsMove(AMOTION_EVENT_ACTION_MOVE, downTime, {pointer}, mSeq);
+ // A batched MotionEvent only has a single event id, currently determined when the
+ // MotionEvent is initialized. Therefore, to pass the eventId comparisons inside
+ // verifyArgsEqualToEvent, we need to override the event id of the published args to match
+ // the event id of the first sample inside the MotionEvent.
+ if (i == 0) {
+ firstSampleId = argsMove.eventId;
+ }
+ argsMove.eventId = firstSampleId;
+ publishTimes.push_back(systemTime(SYSTEM_TIME_MONOTONIC));
+ argsMoves.push_back(argsMove);
+ publishMotionEvent(*mPublisher, argsMove);
+ }
+
+ std::vector<MotionEvent> singleSampledMotionEvents;
+
+ // Unblock Looper
+ {
+ std::scoped_lock l(mLock);
+ mLooperMayProceed = true;
+ }
+ mNotifyLooperMayProceed.notify_all();
+
+ // We have no control over the socket behavior, so the consumer can receive
+ // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a
+ // mix of those)
+ while (singleSampledMotionEvents.size() != nSamples) {
+ const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent =
+ mMotionEvents.popWithTimeout(TIMEOUT);
+ // The events received by these calls are never null
+ std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent);
+ singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(),
+ splitMotionEvents.end());
+ }
+
+ // Consumer can choose to finish events in any order. For simplicity,
+ // we verify the events in sequence (since that is how the test is implemented).
+ for (size_t i = 0; i < nSamples; ++i) {
+ verifyArgsEqualToEvent(argsMoves[i], singleSampledMotionEvents[i]);
+ verifyFinishedSignal(*mPublisher, publishedSequenceNumbers.front(), publishTimes[i]);
+ publishedSequenceNumbers.pop();
+ }
+}
+
void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove(
nsecs_t downTime) {
uint32_t seq = mSeq++;
@@ -814,4 +934,8 @@
ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishAndConsumeSinglePointer) {
+ publishAndConsumeSinglePointerMultipleSamples(3);
+}
+
} // namespace android
diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
index cc41eeb..0542f39 100644
--- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -167,7 +167,8 @@
.y(predictionPoints[i].position[0])
.axis(AMOTION_EVENT_AXIS_PRESSURE, predictionPoints[i].pressure)
.buildCoords();
- predictionEvent.addSample(predictionPoints[i].targetTimestamp, &coords);
+ predictionEvent.addSample(predictionPoints[i].targetTimestamp, &coords,
+ predictionEvent.getId());
}
return predictionEvent;
}
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index d077760..106e686 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -70,7 +70,7 @@
}
TEST(JerkTrackerTest, JerkReadiness) {
- JerkTracker jerkTracker(true);
+ JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1);
EXPECT_FALSE(jerkTracker.jerkMagnitude());
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
EXPECT_FALSE(jerkTracker.jerkMagnitude());
@@ -87,7 +87,8 @@
}
TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) {
- JerkTracker jerkTracker(true);
+ const float alpha = .5;
+ JerkTracker jerkTracker(/*normalizedDt=*/true, alpha);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -118,11 +119,13 @@
* y'': 3 -> -15
* y''': -18
*/
- EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-50, -18));
+ const float newJerk = (1 - alpha) * std::hypot(10, -1) + alpha * std::hypot(-50, -18);
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
}
TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) {
- JerkTracker jerkTracker(false);
+ const float alpha = .5;
+ JerkTracker jerkTracker(/*normalizedDt=*/false, alpha);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/10, 25, 53);
jerkTracker.pushSample(/*timestamp=*/20, 30, 60);
@@ -153,11 +156,12 @@
* y'': .03 -> -.125 (delta above, divide by 10)
* y''': -.0155 (delta above, divide by 10)
*/
- EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-.0375, -.0155));
+ const float newJerk = (1 - alpha) * std::hypot(.01, -.001) + alpha * std::hypot(-.0375, -.0155);
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
}
TEST(JerkTrackerTest, JerkCalculationAfterReset) {
- JerkTracker jerkTracker(true);
+ JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -291,15 +295,22 @@
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
- // Jerk is medium (1.05 normalized, which is halfway between LOW_JANK and HIGH_JANK)
- predictor.record(getMotionEvent(DOWN, 0, 5.2, 20ms));
- predictor.record(getMotionEvent(MOVE, 0, 11.5, 30ms));
- predictor.record(getMotionEvent(MOVE, 0, 22, 40ms));
- predictor.record(getMotionEvent(MOVE, 0, 37.75, 50ms));
- predictor.record(getMotionEvent(MOVE, 0, 59.8, 60ms));
+ // Create another instance of TfLiteMotionPredictorModel to read config details.
+ std::unique_ptr<TfLiteMotionPredictorModel> testTfLiteModel =
+ TfLiteMotionPredictorModel::create();
+ const float mediumJerk =
+ (testTfLiteModel->config().lowJerk + testTfLiteModel->config().highJerk) / 2;
+ const float a = 3; // initial acceleration
+ const float b = 4; // initial velocity
+ const float c = 5; // initial position
+ predictor.record(getMotionEvent(DOWN, 0, c, 20ms));
+ predictor.record(getMotionEvent(MOVE, 0, c + b, 30ms));
+ predictor.record(getMotionEvent(MOVE, 0, c + 2 * b + a, 40ms));
+ predictor.record(getMotionEvent(MOVE, 0, c + 3 * b + 3 * a + mediumJerk, 50ms));
+ predictor.record(getMotionEvent(MOVE, 0, c + 4 * b + 6 * a + 4 * mediumJerk, 60ms));
std::unique_ptr<MotionEvent> predicted = predictor.predict(82 * NSEC_PER_MSEC);
EXPECT_NE(nullptr, predicted);
- // Halfway between LOW_JANK and HIGH_JANK means that half of the predictions
+ // Halfway between LOW_JERK and HIGH_JERK means that half of the predictions
// will be pruned. If model prediction window is close enough to predict()
// call time window, then half of the model predictions (5/2 -> 2) will be
// ouputted.
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
new file mode 100644
index 0000000..26dee39
--- /dev/null
+++ b/libs/input/tests/Resampler_test.cpp
@@ -0,0 +1,873 @@
+/**
+ * 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.
+ */
+
+#include <input/Resampler.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/InputTransport.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::literals::chrono_literals;
+
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+ /**
+ * Converts from Pointer to PointerCoords. Enables calling LegacyResampler methods and
+ * assertions only with the relevant data for tests.
+ */
+ operator PointerCoords() const;
+};
+
+Pointer::operator PointerCoords() const {
+ PointerCoords pointerCoords;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ pointerCoords.isResampled = isResampled;
+ return pointerCoords;
+}
+
+struct InputSample {
+ std::chrono::milliseconds eventTime{0};
+ std::vector<Pointer> pointers{};
+
+ explicit InputSample(std::chrono::milliseconds eventTime, const std::vector<Pointer>& pointers)
+ : eventTime{eventTime}, pointers{pointers} {}
+ /**
+ * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator InputMessage() const;
+};
+
+InputSample::operator InputMessage() const {
+ InputMessageBuilder messageBuilder =
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .eventTime(std::chrono::nanoseconds{eventTime}.count())
+ .source(AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(0);
+
+ for (const Pointer& pointer : pointers) {
+ messageBuilder.pointer(
+ PointerBuilder{pointer.id, pointer.toolType}.x(pointer.x).y(pointer.y).isResampled(
+ pointer.isResampled));
+ }
+ return messageBuilder.build();
+}
+
+struct InputStream {
+ std::vector<InputSample> samples{};
+ int32_t action{0};
+ DeviceId deviceId{0};
+ /**
+ * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator MotionEvent() const;
+};
+
+InputStream::operator MotionEvent() const {
+ const InputSample& firstSample{*samples.begin()};
+ MotionEventBuilder motionEventBuilder =
+ MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
+ .downTime(0)
+ .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count())
+ .deviceId(deviceId);
+ for (const Pointer& pointer : firstSample.pointers) {
+ const PointerBuilder pointerBuilder =
+ PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y);
+ motionEventBuilder.pointer(pointerBuilder);
+ }
+ MotionEvent motionEvent = motionEventBuilder.build();
+ const size_t numSamples = samples.size();
+ for (size_t i = 1; i < numSamples; ++i) {
+ std::vector<PointerCoords> pointersCoords{samples[i].pointers.begin(),
+ samples[i].pointers.end()};
+ motionEvent.addSample(static_cast<std::chrono::nanoseconds>(samples[i].eventTime).count(),
+ pointersCoords.data(), motionEvent.getId());
+ }
+ return motionEvent;
+}
+
+} // namespace
+
+/**
+ * The testing setup assumes an input rate of 200 Hz and a display rate of 60 Hz. This implies that
+ * input events are received every 5 milliseconds, while the display consumes batched events every
+ * ~16 milliseconds. The resampler's RESAMPLE_LATENCY constant determines the resample time, which
+ * is calculated as frameTime - RESAMPLE_LATENCY. resampleTime specifies the time used for
+ * resampling. For example, if the desired frame time consumption is ~16 milliseconds, the resample
+ * time would be ~11 milliseconds. Consequenly, the last added sample to the motion event has an
+ * event time of ~11 milliseconds. Note that there are specific scenarios where resampleMotionEvent
+ * is not called with a multiple of ~16 milliseconds. These cases are primarily for data addition
+ * or to test other functionalities of the resampler.
+ *
+ * Coordinates are calculated using linear interpolation (lerp) based on the last two available
+ * samples. Linear interpolation is defined as (a + alpha*(b - a)). Let t_b and t_a be the
+ * timestamps of samples a and b, respectively. The interpolation factor alpha is calculated as
+ * (resampleTime - t_a) / (t_b - t_a). The value of alpha determines whether the resampled
+ * coordinates are interpolated or extrapolated. If alpha falls within the semi-closed interval [0,
+ * 1), the coordinates are interpolated. If alpha is greater than or equal to 1, the coordinates are
+ * extrapolated.
+ *
+ * The timeline below depics an interpolation scenario
+ * -----------------------------------|---------|---------|---------|----------
+ * 10ms 11ms 15ms 16ms
+ * MOVE | MOVE |
+ * resampleTime frameTime
+ * Based on the timeline alpha is (11 - 10)/(15 - 10) = 1/5. Thus, coordinates are interpolated.
+ *
+ * The following timeline portrays an extrapolation scenario
+ * -------------------------|---------|---------|-------------------|----------
+ * 5ms 10ms 11ms 16ms
+ * MOVE MOVE | |
+ * resampleTime frameTime
+ * Likewise, alpha = (11 - 5)/(10 - 5) = 6/5. Hence, coordinates are extrapolated.
+ *
+ * If a motion event was resampled, the tests will check that the following conditions are satisfied
+ * to guarantee resampling correctness:
+ * - The motion event metadata must not change.
+ * - The number of samples in the motion event must only increment by 1.
+ * - The resampled values must be at the end of motion event coordinates.
+ * - The rasamples values must be near the hand calculations.
+ * - The resampled time must be the most recent one in motion event.
+ */
+class ResamplerTest : public testing::Test {
+protected:
+ ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {}
+
+ ~ResamplerTest() override {}
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ std::unique_ptr<Resampler> mResampler;
+
+ /**
+ * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample
+ * member function.
+ * @param beforeCall MotionEvent before passing it to resampleMotionEvent
+ * @param afterCall MotionEvent after passing it to resampleMotionEvent
+ */
+ void assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall);
+
+ /**
+ * Asserts the MotionEvent is resampled by checking an increment in history size and that the
+ * resampled coordinates are near the expected ones.
+ */
+ void assertMotionEventIsResampledAndCoordsNear(
+ const MotionEvent& original, const MotionEvent& resampled,
+ const std::vector<PointerCoords>& expectedCoords);
+
+ void assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled);
+};
+
+void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall) {
+ EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId());
+ EXPECT_EQ(beforeCall.getAction(), afterCall.getAction());
+ EXPECT_EQ(beforeCall.getActionButton(), afterCall.getActionButton());
+ EXPECT_EQ(beforeCall.getButtonState(), afterCall.getButtonState());
+ EXPECT_EQ(beforeCall.getFlags(), afterCall.getFlags());
+ EXPECT_EQ(beforeCall.getEdgeFlags(), afterCall.getEdgeFlags());
+ EXPECT_EQ(beforeCall.getClassification(), afterCall.getClassification());
+ EXPECT_EQ(beforeCall.getPointerCount(), afterCall.getPointerCount());
+ EXPECT_EQ(beforeCall.getMetaState(), afterCall.getMetaState());
+ EXPECT_EQ(beforeCall.getSource(), afterCall.getSource());
+ EXPECT_EQ(beforeCall.getXPrecision(), afterCall.getXPrecision());
+ EXPECT_EQ(beforeCall.getYPrecision(), afterCall.getYPrecision());
+ EXPECT_EQ(beforeCall.getDownTime(), afterCall.getDownTime());
+ EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId());
+}
+
+void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(
+ const MotionEvent& original, const MotionEvent& resampled,
+ const std::vector<PointerCoords>& expectedCoords) {
+ assertMotionEventMetaDataDidNotMutate(original, resampled);
+
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t resampledSampleSize = resampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize + 1, resampledSampleSize);
+
+ const size_t numPointers = resampled.getPointerCount();
+ const size_t beginLatestSample = resampledSampleSize - 1;
+ for (size_t i = 0; i < numPointers; ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(original.getPointerId(i), resampled.getPointerId(i));
+ EXPECT_EQ(original.getToolType(i), resampled.getToolType(i));
+
+ const PointerCoords& resampledCoords =
+ resampled.getSamplePointerCoords()[beginLatestSample * numPointers + i];
+
+ EXPECT_TRUE(resampledCoords.isResampled);
+ EXPECT_NEAR(expectedCoords[i].getX(), resampledCoords.getX(), EPSILON);
+ EXPECT_NEAR(expectedCoords[i].getY(), resampledCoords.getY(), EPSILON);
+ }
+}
+
+void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled) {
+ assertMotionEventMetaDataDidNotMutate(original, notResampled);
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t notResampledSampleSize = notResampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize, notResampledSampleSize);
+}
+
+TEST_F(ResamplerTest, NonResampledAxesArePreserved) {
+ constexpr float TOUCH_MAJOR_VALUE = 1.0f;
+
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ constexpr std::chrono::nanoseconds eventTime{10ms};
+ PointerCoords pointerCoords{};
+ pointerCoords.isResampled = false;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 2.0f);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2.0f);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, TOUCH_MAJOR_VALUE);
+
+ motionEvent.addSample(eventTime.count(), &pointerCoords, motionEvent.getId());
+
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) {
+ MotionEvent motionFromFirstDevice =
+ InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 0};
+
+ mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr);
+
+ MotionEvent motionFromSecondDevice =
+ InputStream{{InputSample{11ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 1};
+ const MotionEvent originalMotionEvent = motionFromSecondDevice;
+
+ mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr);
+ // The MotionEvent should not be resampled because the second event came from a different device
+ // than the previous event.
+ assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice);
+}
+
+TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 1.2f,
+ .y = 2.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(10'500'000ns, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+/**
+ * Tests extrapolation given two MotionEvents with a single sample.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 4.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 5.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 3.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 4.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{9ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{26ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(32ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{25ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(48ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.4f,
+ .y = 4.8f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerSingleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 2.2f, .y = 2.2f, .isResampled = true},
+ Pointer{.x = 3.2f, .y = 3.2f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerSingleSampleExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerMultipleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false},
+ {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerMultipleSampleExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 1.4f, .y = 1.4f, .isResampled = true},
+ Pointer{.x = 2.4f, .y = 2.4f, .isResampled = true}});
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{25ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage secondFutureSample =
+ InputSample{30ms,
+ {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false},
+ {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false},
+ {.id = 2, .x = 7.0f, .y = 7.0f, .isResampled = false}}};
+
+ const MotionEvent originalSecondMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(32ms, secondMotionEvent, &secondFutureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalSecondMotionEvent, secondMotionEvent,
+ {Pointer{.x = 3.8f, .y = 3.8f, .isResampled = true},
+ Pointer{.x = 4.8f, .y = 4.8f, .isResampled = true},
+ Pointer{.x = 5.8f, .y = 5.8f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 1, .x = 5.0f, .y = 5.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false},
+ {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdsExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeInterpolation) {
+ MotionEvent motionEvent = InputStream{{InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::FINGER,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample = InputSample{15ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 3.0,
+ .y = 3.0,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::STYLUS,
+ .x = 4.0,
+ .y = 4.0,
+ .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeExtrapolation) {
+ MotionEvent firstMotionEvent = InputStream{{InputSample{5ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::FINGER,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent = InputStream{{InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::STYLUS,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeInterpolation) {
+ MotionEvent motionEvent = InputStream{{InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample = InputSample{15ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 3.0,
+ .y = 3.0,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 4.0,
+ .y = 4.0,
+ .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeExtrapolation) {
+ MotionEvent motionEvent = InputStream{{InputSample{5ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 3.0f,
+ .y = 3.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 4.0f,
+ .y = 4.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+} // namespace android
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
new file mode 100644
index 0000000..dd2e40c
--- /dev/null
+++ b/libs/input/tests/TestEventMatchers.h
@@ -0,0 +1,110 @@
+/**
+ * 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 <ostream>
+
+#include <input/Input.h>
+
+namespace android {
+
+/**
+ * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally,
+ * implementations must not be duplicated.
+ * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput.
+ */
+
+class WithDeviceIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {}
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDeviceId == event.getDeviceId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+ const DeviceId mDeviceId;
+};
+
+inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+ return WithDeviceIdMatcher(deviceId);
+}
+
+class WithMotionActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ bool matches = mAction == event.getAction();
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with motion action " << MotionEvent::actionToString(mAction);
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ *os << " and FLAG_CANCELED";
+ }
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+inline WithMotionActionMatcher WithMotionAction(int32_t action) {
+ return WithMotionActionMatcher(action);
+}
+
+class MotionEventIsResampledMatcher {
+public:
+ using is_gtest_matcher = void;
+
+ bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const {
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ const size_t numPointers = motionEvent.getPointerCount();
+ if (numPointers <= 0 || numSamples <= 0) {
+ return false;
+ }
+ for (size_t i = 0; i < numPointers; ++i) {
+ const PointerCoords& pointerCoords =
+ motionEvent.getSamplePointerCoords()[numSamples * numPointers + i];
+ if (!pointerCoords.isResampled) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "MotionEvent is resampled."; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "MotionEvent is not resampled."; }
+};
+
+inline MotionEventIsResampledMatcher MotionEventIsResampled() {
+ return MotionEventIsResampledMatcher();
+}
+} // namespace android
diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp
new file mode 100644
index 0000000..26a0ca2
--- /dev/null
+++ b/libs/input/tests/TestInputChannel.cpp
@@ -0,0 +1,102 @@
+/**
+ * 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.
+ */
+
+#define LOG_TAG "TestInputChannel"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include <TestInputChannel.h>
+
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <array>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace {
+
+/**
+ * Returns a stub file descriptor by opening a socket pair and closing one of the fds. The returned
+ * fd can be used to construct an InputChannel.
+ */
+base::unique_fd generateFileDescriptor() {
+ std::array<int, 2> kFileDescriptors;
+ LOG_IF(FATAL, ::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, kFileDescriptors.data()) != 0)
+ << "TestInputChannel. Failed to create socket pair.";
+ LOG_IF(FATAL, ::close(kFileDescriptors[1]) != 0)
+ << "TestInputChannel. Failed to close file descriptor.";
+ return base::unique_fd{kFileDescriptors[0]};
+}
+} // namespace
+
+// --- TestInputChannel ---
+
+TestInputChannel::TestInputChannel(const std::string& name)
+ : InputChannel{name, generateFileDescriptor(), sp<BBinder>::make()} {}
+
+void TestInputChannel::enqueueMessage(const InputMessage& message) {
+ mReceivedMessages.push(message);
+}
+
+status_t TestInputChannel::sendMessage(const InputMessage* message) {
+ LOG_IF(FATAL, message == nullptr)
+ << "TestInputChannel " << getName() << ". No message was passed to sendMessage.";
+
+ mSentMessages.push(*message);
+ return OK;
+}
+
+base::Result<InputMessage> TestInputChannel::receiveMessage() {
+ if (mReceivedMessages.empty()) {
+ return base::Error(WOULD_BLOCK);
+ }
+ InputMessage message = mReceivedMessages.front();
+ mReceivedMessages.pop();
+ return message;
+}
+
+bool TestInputChannel::probablyHasInput() const {
+ return !mReceivedMessages.empty();
+}
+
+void TestInputChannel::assertFinishMessage(uint32_t seq, bool handled) {
+ ASSERT_FALSE(mSentMessages.empty())
+ << "TestInputChannel " << getName() << ". Cannot assert. mSentMessages is empty.";
+
+ const InputMessage& finishMessage = mSentMessages.front();
+
+ EXPECT_EQ(finishMessage.header.seq, seq)
+ << "TestInputChannel " << getName()
+ << ". Sequence mismatch. Message seq: " << finishMessage.header.seq
+ << " Expected seq: " << seq;
+
+ EXPECT_EQ(finishMessage.body.finished.handled, handled)
+ << "TestInputChannel " << getName()
+ << ". Handled value mismatch. Message val: " << std::boolalpha
+ << finishMessage.body.finished.handled << "Expected val: " << handled
+ << std::noboolalpha;
+ mSentMessages.pop();
+}
+
+void TestInputChannel::assertNoSentMessages() const {
+ ASSERT_TRUE(mSentMessages.empty());
+}
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/TestInputChannel.h b/libs/input/tests/TestInputChannel.h
new file mode 100644
index 0000000..43253ec
--- /dev/null
+++ b/libs/input/tests/TestInputChannel.h
@@ -0,0 +1,66 @@
+/**
+ * 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 <queue>
+#include <string>
+
+#include <android-base/result.h>
+#include <gtest/gtest.h>
+#include <input/InputTransport.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class TestInputChannel final : public InputChannel {
+public:
+ explicit TestInputChannel(const std::string& name);
+
+ /**
+ * Enqueues a message in mReceivedMessages.
+ */
+ void enqueueMessage(const InputMessage& message);
+
+ /**
+ * Pushes message to mSentMessages. In the default implementation, InputChannel sends messages
+ * through a file descriptor. TestInputChannel, on the contrary, stores sent messages in
+ * mSentMessages for assertion reasons.
+ */
+ status_t sendMessage(const InputMessage* message) override;
+
+ /**
+ * Returns an InputMessage from mReceivedMessages. This is done instead of retrieving data
+ * directly from fd.
+ */
+ base::Result<InputMessage> receiveMessage() override;
+
+ /**
+ * Returns if mReceivedMessages is not empty.
+ */
+ bool probablyHasInput() const override;
+
+ void assertFinishMessage(uint32_t seq, bool handled);
+
+ void assertNoSentMessages() const;
+
+private:
+ // InputMessages received by the endpoint.
+ std::queue<InputMessage> mReceivedMessages;
+ // InputMessages sent by the endpoint.
+ std::queue<InputMessage> mSentMessages;
+};
+} // namespace android
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index e3be3bc..d0ca78e 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -129,7 +129,7 @@
std::vector<DisplayConfigImpl> modesPerDisplay[size];
ui::DisplayConnectionType displayConnectionTypes[size];
int numModes = 0;
- for (int i = 0; i < size; ++i) {
+ for (size_t i = 0; i < size; ++i) {
ui::StaticDisplayInfo staticInfo;
if (const status_t status =
SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
@@ -151,7 +151,7 @@
numModes += modes.size();
modesPerDisplay[i].reserve(modes.size());
- for (int j = 0; j < modes.size(); ++j) {
+ for (size_t j = 0; j < modes.size(); ++j) {
const ui::DisplayMode& mode = modes[j];
modesPerDisplay[i].emplace_back(
DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(),
@@ -224,7 +224,7 @@
CHECK_NOT_NULL(display);
DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
float maxFps = 0.0;
- for (int i = 0; i < impl->numConfigs; ++i) {
+ for (size_t i = 0; i < impl->numConfigs; ++i) {
maxFps = std::max(maxFps, impl->configs[i].fps);
}
return maxFps;
@@ -261,7 +261,7 @@
for (size_t i = 0; i < impl->numConfigs; i++) {
auto* config = impl->configs + i;
- if (config->id == info.activeDisplayModeId) {
+ if (info.activeDisplayModeId >= 0 && config->id == (size_t)info.activeDisplayModeId) {
*outConfig = reinterpret_cast<ADisplayConfig*>(config);
return OK;
}
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 099f47d..f1453bd 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -98,11 +98,25 @@
* is created in a detached state, and attachToContext must be called before
* calls to updateTexImage.
*/
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ SurfaceTexture(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
+
+ SurfaceTexture(uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
+
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
bool useFenceSync, bool isControlledByApp);
SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
bool isControlledByApp);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
/**
* updateTexImage acquires the most recently queued buffer, and sets the
@@ -499,6 +513,8 @@
friend class EGLConsumer;
private:
+ void initialize();
+
// Proxy listener to avoid having SurfaceTexture directly implement FrameAvailableListener as it
// is extending ConsumerBase which also implements FrameAvailableListener.
class FrameAvailableListenerProxy : public ConsumerBase::FrameAvailableListener {
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 3a09204..ce232cc 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -35,6 +35,49 @@
static const mat4 mtxIdentity;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+SurfaceTexture::SurfaceTexture(uint32_t tex, uint32_t texTarget, bool useFenceSync,
+ bool isControlledByApp)
+ : ConsumerBase(isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mOpMode(OpMode::attachedToGL) {
+ initialize();
+}
+
+SurfaceTexture::SurfaceTexture(uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mOpMode(OpMode::detached) {
+ initialize();
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
: ConsumerBase(bq, isControlledByApp),
@@ -53,11 +96,7 @@
mTexTarget(texTarget),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mOpMode(OpMode::attachedToGL) {
- SFT_LOGV("SurfaceTexture");
-
- memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
-
- mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+ initialize();
}
SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
@@ -78,11 +117,7 @@
mTexTarget(texTarget),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mOpMode(OpMode::detached) {
- SFT_LOGV("SurfaceTexture");
-
- memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
-
- mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+ initialize();
}
status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
@@ -531,4 +566,12 @@
}
#endif
+void SurfaceTexture::initialize() {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
} // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index dd78049..ca41346 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -196,10 +196,10 @@
return BAD_VALUE;
}
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -248,10 +248,10 @@
if (!buffer) return BAD_VALUE;
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -277,10 +277,10 @@
int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) {
if (!buffer || !outPlanes) return BAD_VALUE;
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
index 6cf8291..937ff02 100644
--- a/libs/nativewindow/tests/ANativeWindowTest.cpp
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -50,9 +50,14 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mItemConsumer = new BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN);
+ mWindow = new TestableSurface(mItemConsumer->getSurface()->getIGraphicBufferProducer());
+#else
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN);
mWindow = new TestableSurface(mProducer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU);
EXPECT_EQ(0, success);
}
@@ -64,10 +69,12 @@
const int success = native_window_api_disconnect(mWindow.get(), NATIVE_WINDOW_API_CPU);
EXPECT_EQ(0, success);
}
+
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<BufferItemConsumer> mItemConsumer;
-
sp<TestableSurface> mWindow;
};
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 4a04467..d248ea0 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -100,11 +100,14 @@
"skia/debug/SkiaCapture.cpp",
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/GainmapFactory.cpp",
"skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
"skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
+ "skia/filters/EdgeExtensionShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index 6f2a96a..8d0fbba 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#include <common/trace.h>
#include <log/log.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/impl/ExternalTexture.h>
#include <ui/GraphicBuffer.h>
-#include <utils/Trace.h>
namespace android::renderengine::impl {
@@ -35,7 +35,7 @@
}
void ExternalTexture::remapBuffer() {
- ATRACE_CALL();
+ SFTRACE_CALL();
{
auto buf = mBuffer;
mRenderEngine.unmapExternalTextureBuffer(std::move(buf));
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index bc3976d..907590a 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -21,6 +21,7 @@
#include "skia/GraphiteVkRenderEngine.h"
#include "skia/SkiaGLRenderEngine.h"
#include "threaded/RenderEngineThreaded.h"
+#include "ui/GraphicTypes.h"
#include <com_android_graphics_surfaceflinger_flags.h>
#include <cutils/properties.h>
@@ -101,17 +102,34 @@
base::unique_fd&& bufferFence) {
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
- updateProtectedContext(layers, buffer);
+ updateProtectedContext(layers, {buffer.get()});
drawLayersInternal(std::move(resultPromise), display, layers, buffer, std::move(bufferFence));
return resultFuture;
}
+ftl::Future<FenceResult> RenderEngine::drawGainmap(
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
+ std::future<FenceResult> resultFuture = resultPromise->get_future();
+ updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
+ drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
+ std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
+ return resultFuture;
+}
+
void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer) {
+ vector<const ExternalTexture*> buffers) {
const bool needsProtectedContext =
- (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) ||
- std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) {
- const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer;
+ std::any_of(layers.begin(), layers.end(),
+ [](const LayerSettings& layer) {
+ const std::shared_ptr<ExternalTexture>& buffer =
+ layer.source.buffer.buffer;
+ return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ }) ||
+ std::any_of(buffers.begin(), buffers.end(), [](const ExternalTexture* buffer) {
return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
});
useProtectedContext(needsProtectedContext);
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index e1a6f6a..f84db0b 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -57,6 +57,7 @@
"libui",
"libutils",
"server_configurable_flags",
+ "libtracing_perfetto",
],
data: ["resources/*"],
diff --git a/libs/renderengine/benchmark/AndroidTest.xml b/libs/renderengine/benchmark/AndroidTest.xml
new file mode 100644
index 0000000..3b923cb
--- /dev/null
+++ b/libs/renderengine/benchmark/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for librenderengine_bench.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native-metric" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="librenderengine_bench->/data/local/tmp/librenderengine_bench" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/local/tmp" />
+ <option name="benchmark-module-name" value="librenderengine_bench" />
+ <option name="file-exclusion-filter-regex" value=".*\.config$" />
+ <option name="file-exclusion-filter-regex" value=".*/resources/.*" />
+ </test>
+</configuration>
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 05a2063..a9264b3 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -29,6 +29,16 @@
using namespace android;
using namespace android::renderengine;
+// To run tests:
+/**
+ * mmm frameworks/native/libs/renderengine/benchmark;\
+ * adb push $OUT/data/benchmarktest/librenderengine_bench/librenderengine_bench
+ * /data/benchmarktest/librenderengine_bench/librenderengine_bench;\
+ * adb shell /data/benchmarktest/librenderengine_bench/librenderengine_bench
+ *
+ * (64-bit devices: out directory contains benchmarktest64 instead of benchmarktest)
+ */
+
///////////////////////////////////////////////////////////////////////////////
// Helpers for calling drawLayers
///////////////////////////////////////////////////////////////////////////////
@@ -64,14 +74,15 @@
return std::pair<uint32_t, uint32_t>(width, height);
}
-static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded,
- RenderEngine::GraphicsApi graphicsApi) {
+static std::unique_ptr<RenderEngine> createRenderEngine(
+ RenderEngine::Threaded threaded, RenderEngine::GraphicsApi graphicsApi,
+ RenderEngine::BlurAlgorithm blurAlgorithm = RenderEngine::BlurAlgorithm::KAWASE) {
auto args = RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
.setEnableProtectedContext(true)
.setPrecacheToneMapperShaderOnly(false)
- .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE)
+ .setBlurAlgorithm(blurAlgorithm)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
.setThreaded(threaded)
.setGraphicsApi(graphicsApi)
@@ -172,28 +183,67 @@
}
}
+/**
+ * Return a buffer with the image in the provided path, relative to the executable directory
+ */
+static std::shared_ptr<ExternalTexture> createTexture(RenderEngine& re, const char* relPathImg) {
+ // Initially use cpu access so we can decode into it with AImageDecoder.
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer =
+ allocateBuffer(re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
+ std::string fileName = base::GetExecutableDirectory().append(relPathImg);
+ renderenginebench::decode(fileName.c_str(), srcBuffer->getBuffer());
+ // Now copy into GPU-only buffer for more realistic timing.
+ srcBuffer = copyBuffer(re, srcBuffer, 0, "source");
+ return srcBuffer;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Benchmarks
///////////////////////////////////////////////////////////////////////////////
+constexpr char kHomescreenPath[] = "/resources/homescreen.png";
+
+/**
+ * Draw a layer with texture and no additional shaders as a baseline to evaluate a shader's impact
+ * on performance
+ */
template <class... Args>
-void BM_blur(benchmark::State& benchState, Args&&... args) {
+void BM_homescreen(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();
- auto srcBuffer =
- allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
- {
- std::string srcImage = base::GetExecutableDirectory();
- srcImage.append("/resources/homescreen.png");
- renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+ auto srcBuffer = createTexture(*re, kHomescreenPath);
- // Now copy into GPU-only buffer for more realistic timing.
- srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
- }
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+ benchDrawLayers(*re, layers, benchState, "homescreen");
+}
+
+template <class... Args>
+void BM_homescreen_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)));
+
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer = createTexture(*re, kHomescreenPath);
const FloatRect layerRect(0, 0, width, height);
LayerSettings layer{
@@ -221,8 +271,55 @@
};
auto layers = std::vector<LayerSettings>{layer, blurLayer};
- benchDrawLayers(*re, layers, benchState, "blurred");
+ benchDrawLayers(*re, layers, benchState, "homescreen_blurred");
}
-BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES,
+template <class... Args>
+void BM_homescreen_edgeExtension(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)));
+
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer = createTexture(*re, kHomescreenPath);
+
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = FloatRect(0, 0, width, height),
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ // Part of the screen is not covered by the texture but
+ // will be filled in by the shader
+ .textureTransform =
+ mat4(mat3(),
+ vec3(width * 0.3f, height * 0.3f, 0.0f)),
+ },
+ },
+ .alpha = half(1.0f),
+ .edgeExtensionEffect =
+ EdgeExtensionEffect(/* left */ true,
+ /* right */ false, /* top */ true, /* bottom */ false),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+ benchDrawLayers(*re, layers, benchState, "homescreen_edge_extension");
+}
+
+BENCHMARK_CAPTURE(BM_homescreen_blur, gaussian, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::GAUSSIAN);
+
+BENCHMARK_CAPTURE(BM_homescreen_blur, kawase, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE);
+
+BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER);
+
+BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL);
+
+BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES,
RenderEngine::GraphicsApi::GL);
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index b640983..280ec19 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -102,6 +102,9 @@
Local,
};
TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap;
+
+ // For now, meaningful primarily when the TonemappingStrategy is Local
+ float targetHdrSdrRatio = 1.f;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 8ac0af4..859ae8b 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -31,6 +31,7 @@
#include <ui/ShadowSettings.h>
#include <ui/StretchEffect.h>
#include <ui/Transform.h>
+#include "ui/EdgeExtensionEffect.h"
#include <iosfwd>
@@ -134,6 +135,7 @@
mat4 blurRegionTransform = mat4();
StretchEffect stretchEffect;
+ EdgeExtensionEffect edgeExtensionEffect;
// Name associated with the layer for debugging purposes.
std::string name;
@@ -183,7 +185,9 @@
lhs.skipContentDraw == rhs.skipContentDraw && lhs.shadow == rhs.shadow &&
lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
lhs.blurRegionTransform == rhs.blurRegionTransform &&
- lhs.stretchEffect == rhs.stretchEffect && lhs.whitePointNits == rhs.whitePointNits;
+ lhs.stretchEffect == rhs.stretchEffect &&
+ lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
+ lhs.whitePointNits == rhs.whitePointNits;
}
static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
@@ -254,6 +258,10 @@
*os << "\n}";
}
+static inline void PrintTo(const EdgeExtensionEffect& effect, ::std::ostream* os) {
+ *os << effect;
+}
+
static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
*os << "LayerSettings for '" << settings.name.c_str() << "' {";
*os << "\n .geometry = ";
@@ -285,6 +293,10 @@
*os << "\n .stretchEffect = ";
PrintTo(settings.stretchEffect, os);
}
+
+ if (settings.edgeExtensionEffect.hasEffect()) {
+ *os << "\n .edgeExtensionEffect = " << settings.edgeExtensionEffect;
+ }
*os << "\n .whitePointNits = " << settings.whitePointNits;
*os << "\n}";
}
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 7207394..0fd982e 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -97,6 +97,7 @@
bool cacheImageDimmedLayers = true;
bool cacheClippedLayers = true;
bool cacheShadowLayers = true;
+ bool cacheEdgeExtension = true;
bool cachePIPImageLayers = true;
bool cacheTransparentImageDimmedLayers = true;
bool cacheClippedDimmedImageLayers = true;
@@ -131,6 +132,7 @@
NONE,
GAUSSIAN,
KAWASE,
+ KAWASE_DUAL_FILTER,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
@@ -207,6 +209,13 @@
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence);
+ virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap);
+
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
// resources used by the most recently drawn frame. If the frame is still
@@ -284,8 +293,7 @@
// Update protectedContext mode depending on whether or not any layer has a protected buffer.
void updateProtectedContext(const std::vector<LayerSettings>&,
- const std::shared_ptr<ExternalTexture>&);
-
+ std::vector<const ExternalTexture*>);
// Attempt to switch RenderEngine into and out of protectedContext mode
virtual void useProtectedContext(bool useProtectedContext) = 0;
@@ -293,6 +301,13 @@
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0;
+
+ virtual void drawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) = 0;
};
struct RenderEngineCreationArgs {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index a8c242a..fb8331d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -46,6 +46,17 @@
ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&,
const std::shared_ptr<ExternalTexture>&,
base::unique_fd&&));
+ MOCK_METHOD7(drawGainmap,
+ ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&,
+ base::borrowed_fd&&,
+ const std::shared_ptr<ExternalTexture>&,
+ base::borrowed_fd&&, float, ui::Dataspace,
+ const std::shared_ptr<ExternalTexture>&));
+ MOCK_METHOD8(drawGainmapInternal,
+ void(const std::shared_ptr<std::promise<FenceResult>>&&,
+ const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&,
+ const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float,
+ ui::Dataspace, const std::shared_ptr<ExternalTexture>&));
MOCK_METHOD5(drawLayersInternal,
void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&,
const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&,
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 8aeef9f..b7b7a4d 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -25,8 +25,8 @@
#include "compat/SkiaBackendTexture.h"
+#include <common/trace.h>
#include <log/log_main.h>
-#include <utils/Trace.h>
namespace android {
namespace renderengine {
@@ -63,7 +63,7 @@
}
sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) {
- ATRACE_CALL();
+ SFTRACE_CALL();
sk_sp<SkImage> image = mBackendTexture->makeImage(alphaType, dataspace, releaseImageProc, this);
// The following ref will be counteracted by releaseProc, when SkImage is discarded.
@@ -75,7 +75,7 @@
}
sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace) {
- ATRACE_CALL();
+ SFTRACE_CALL();
LOG_ALWAYS_FATAL_IF(!mBackendTexture->isOutputBuffer(),
"You can't generate an SkSurface for a read-only texture");
if (!mSurface.get() || mDataspace != dataspace) {
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 74daf47..a570ad0 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -16,9 +16,9 @@
#pragma once
-#include <GrDirectContext.h>
#include <SkImage.h>
#include <SkSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <sys/types.h>
#include <ui/GraphicTypes.h>
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 59b0656..57041ee 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -27,6 +27,8 @@
#include "ui/Rect.h"
#include "utils/Timers.h"
+#include <com_android_graphics_libgui_flags.h>
+
namespace android::renderengine::skia {
namespace {
@@ -619,6 +621,32 @@
}
}
+static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ // Make the layer
+ LayerSettings layer{
+ // Make the layer bigger than the texture
+ .geometry = Geometry{.boundaries = FloatRect(0, 0, displayRect.width(),
+ displayRect.height())},
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .isOpaque = 1,
+ }},
+ // The type of effect does not affect the shader's uniforms, but the layer must have a
+ // valid EdgeExtensionEffect to apply the shader
+ .edgeExtensionEffect =
+ EdgeExtensionEffect(true /* left */, false, false, true /* bottom */),
+ };
+ for (float alpha : {0.5, 0.0, 1.0}) {
+ layer.alpha = alpha;
+ auto layers = std::vector<LayerSettings>{layer};
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+}
+
//
// The collection of shaders cached here were found by using perfetto to record shader compiles
// during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -761,6 +789,12 @@
// Draw layers for b/185569240.
drawClippedLayers(renderengine, display, dstTexture, texture);
}
+
+ if (com::android::graphics::libgui::flags::edge_extension_shader() &&
+ config.cacheEdgeExtension) {
+ drawEdgeExtensionLayers(renderengine, display, dstTexture, texture);
+ drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture);
+ }
}
if (config.cachePIPImageLayers) {
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index 68798bf..a3a43e2 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -21,9 +21,9 @@
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
+#include <common/trace.h>
#include <log/log_main.h>
#include <sync/sync.h>
-#include <utils/Trace.h>
namespace android::renderengine::skia {
@@ -78,7 +78,7 @@
sk_sp<SkSurface> dstSurface) {
sk_sp<GrDirectContext> grContext = context->grDirectContext();
{
- ATRACE_NAME("flush surface");
+ SFTRACE_NAME("flush surface");
// TODO: Investigate feasibility of combining this "surface flush" into the "context flush"
// below.
context->grDirectContext()->flush(dstSurface.get());
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
index b5cb21b..390ad6e 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -23,6 +23,7 @@
#include <include/gpu/graphite/BackendSemaphore.h>
#include <include/gpu/graphite/Context.h>
#include <include/gpu/graphite/Recording.h>
+#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
#include <log/log_main.h>
#include <sync/sync.h>
@@ -77,7 +78,7 @@
base::unique_fd fenceDup(dupedFd);
VkSemaphore waitSemaphore =
getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
- graphite::BackendSemaphore beSemaphore(waitSemaphore);
+ auto beSemaphore = graphite::BackendSemaphores::MakeVulkan(waitSemaphore);
mStagedWaitSemaphores.push_back(beSemaphore);
}
@@ -92,7 +93,7 @@
// This "signal" semaphore is called after rendering, but it is cleaned up in the same mechanism
// as "wait" semaphores from waitFence.
VkSemaphore vkSignalSemaphore = vulkanInterface.createExportableSemaphore();
- graphite::BackendSemaphore backendSignalSemaphore(vkSignalSemaphore);
+ auto backendSignalSemaphore = graphite::BackendSemaphores::MakeVulkan(vkSignalSemaphore);
// Collect all Vk semaphores that DestroySemaphoreInfo needs to own and delete after GPU work.
std::vector<VkSemaphore> vkSemaphoresToCleanUp;
@@ -100,7 +101,8 @@
vkSemaphoresToCleanUp.push_back(vkSignalSemaphore);
}
for (auto backendWaitSemaphore : mStagedWaitSemaphores) {
- vkSemaphoresToCleanUp.push_back(backendWaitSemaphore.getVkSemaphore());
+ vkSemaphoresToCleanUp.push_back(
+ graphite::BackendSemaphores::GetVkSemaphore(backendWaitSemaphore));
}
DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 48270e1..4ef7d5b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -21,19 +21,17 @@
#include "SkiaGLRenderEngine.h"
-#include "compat/SkiaGpuContext.h"
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <GrContextOptions.h>
-#include <GrTypes.h>
#include <android-base/stringprintf.h>
-#include <gl/GrGLInterface.h>
+#include <common/trace.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
-#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
+#include <log/log_main.h>
#include <sync/sync.h>
#include <ui/DebugUtils.h>
-#include <utils/Trace.h>
#include <cmath>
#include <cstdint>
@@ -41,7 +39,7 @@
#include <numeric>
#include "GLExtensions.h"
-#include "log/log_main.h"
+#include "compat/SkiaGpuContext.h"
namespace android {
namespace renderengine {
@@ -332,7 +330,7 @@
void SkiaGLRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) {
if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
- ATRACE_NAME("SkiaGLRenderEngine::waitFence");
+ SFTRACE_NAME("SkiaGLRenderEngine::waitFence");
sync_wait(fenceFd.get(), -1);
}
}
@@ -341,19 +339,19 @@
sk_sp<SkSurface> dstSurface) {
sk_sp<GrDirectContext> grContext = context->grDirectContext();
{
- ATRACE_NAME("flush surface");
+ SFTRACE_NAME("flush surface");
grContext->flush(dstSurface.get());
}
base::unique_fd drawFence = flushGL();
bool requireSync = drawFence.get() < 0;
if (requireSync) {
- ATRACE_BEGIN("Submit(sync=true)");
+ SFTRACE_BEGIN("Submit(sync=true)");
} else {
- ATRACE_BEGIN("Submit(sync=false)");
+ SFTRACE_BEGIN("Submit(sync=false)");
}
bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
- ATRACE_END();
+ SFTRACE_END();
if (!success) {
ALOGE("Failed to flush RenderEngine commands");
// Chances are, something illegal happened (Skia's internal GPU object
@@ -400,7 +398,7 @@
}
base::unique_fd SkiaGLRenderEngine::flushGL() {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!GLExtensions::getInstance().hasNativeFenceSync()) {
return base::unique_fd();
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index bd177e6..7651038 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -20,9 +20,10 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
-#include <GrDirectContext.h>
#include <SkSurface.h>
#include <android-base/thread_annotations.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
@@ -32,7 +33,6 @@
#include "AutoBackendTexture.h"
#include "EGL/egl.h"
-#include "GrContextOptions.h"
#include "SkImageInfo.h"
#include "SkiaRenderEngine.h"
#include "android-base/macros.h"
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index cf995bf..e62639f 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,9 +20,6 @@
#include "SkiaRenderEngine.h"
-#include <GrBackendSemaphore.h>
-#include <GrContextOptions.h>
-#include <GrTypes.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
@@ -54,8 +51,11 @@
#include <SkTileMode.h>
#include <android-base/stringprintf.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <gui/FenceMonitor.h>
-#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <pthread.h>
#include <src/core/SkTraceEventCommon.h>
@@ -64,7 +64,6 @@
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/HdrRenderTypeUtils.h>
-#include <utils/Trace.h>
#include <cmath>
#include <cstdint>
@@ -76,7 +75,9 @@
#include "ColorSpaces.h"
#include "compat/SkiaGpuContext.h"
#include "filters/BlurFilter.h"
+#include "filters/GainmapFactory.h"
#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurDualFilter.h"
#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "filters/MouriMap.h"
@@ -238,12 +239,22 @@
static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
return SkPoint3::Make(vector.x, vector.y, vector.z);
}
+
} // namespace
namespace android {
namespace renderengine {
namespace skia {
+namespace {
+void trace(sp<Fence> fence) {
+ if (SFTRACE_ENABLED()) {
+ static gui::FenceMonitor sMonitor("RE Completion");
+ sMonitor.queueFence(std::move(fence));
+ }
+}
+} // namespace
+
using base::StringAppendF;
std::future<void> SkiaRenderEngine::primeCache(PrimeCacheConfig config) {
@@ -261,7 +272,7 @@
const SkString& description) {
mShadersCachedSinceLastCall++;
mTotalShadersCompiled++;
- ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
+ SFTRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
}
int SkiaRenderEngine::reportShadersCompiled() {
@@ -286,6 +297,11 @@
mBlurFilter = new KawaseBlurFilter();
break;
}
+ case BlurAlgorithm::KAWASE_DUAL_FILTER: {
+ ALOGD("Background Blurs Enabled (Kawase dual-filtering algorithm)");
+ mBlurFilter = new KawaseBlurDualFilter();
+ break;
+ }
default: {
mBlurFilter = nullptr;
break;
@@ -416,7 +432,7 @@
if (isProtectedBuffer || isProtected() || !isGpuSampleable) {
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
// If we were to support caching protected buffers then we will need to switch the
// currently bound context if we are not already using the protected context (and subsequently
@@ -441,7 +457,7 @@
}
void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
iter != mGraphicBufferExternalRefs.end()) {
@@ -498,7 +514,7 @@
}
void SkiaRenderEngine::cleanupPostRender() {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mRenderingMutex);
mTextureCleanupMgr.cleanup();
}
@@ -507,16 +523,24 @@
const RuntimeEffectShaderParameters& parameters) {
// The given surface will be stretched by HWUI via matrix transformation
// which gets similar results for most surfaces
- // Determine later on if we need to leverage the stertch shader within
+ // Determine later on if we need to leverage the stretch shader within
// surface flinger
const auto& stretchEffect = parameters.layer.stretchEffect;
const auto& targetBuffer = parameters.layer.source.buffer.buffer;
+ const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+
auto shader = parameters.shader;
- if (stretchEffect.hasEffect()) {
- const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && parameters.shader) {
+ if (graphicBuffer && parameters.shader) {
+ if (stretchEffect.hasEffect()) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
+ // The given surface requires to be filled outside of its buffer bounds if the edge
+ // extension is required
+ const auto& edgeExtensionEffect = parameters.layer.edgeExtensionEffect;
+ if (edgeExtensionEffect.hasEffect()) {
+ shader = mEdgeExtensionShaderFactory.createSkShader(shader, parameters.layer,
+ parameters.imageBounds);
+ }
}
if (parameters.requiresLinearEffect) {
@@ -525,21 +549,28 @@
static_cast<ui::PixelFormat>(targetBuffer->getPixelFormat()))
: std::nullopt;
- if (parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local) {
- // TODO: Handle color matrix transforms in linear space.
- SkImage* image = parameters.shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
- if (image) {
- static MouriMap kMapper;
- const float ratio = getHdrRenderType(parameters.layer.sourceDataspace, format) ==
- HdrRenderType::GENERIC_HDR
- ? 1.0f
- : parameters.layerDimmingRatio;
- return kMapper.mouriMap(getActiveContext(), parameters.shader, ratio);
- }
+ const auto hdrType = getHdrRenderType(parameters.layer.sourceDataspace, format,
+ parameters.layerDimmingRatio);
+
+ const auto usingLocalTonemap =
+ parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local &&
+ hdrType != HdrRenderType::SDR &&
+ shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr) &&
+ (hdrType != HdrRenderType::DISPLAY_HDR ||
+ parameters.display.targetHdrSdrRatio < parameters.layerDimmingRatio);
+ if (usingLocalTonemap) {
+ const float inputRatio =
+ hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio;
+ static MouriMap kMapper;
+ shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio,
+ parameters.display.targetHdrSdrRatio);
}
+ // disable tonemapping if we already locally tonemapped
+ auto inputDataspace =
+ usingLocalTonemap ? parameters.outputDataSpace : parameters.layer.sourceDataspace;
auto effect =
- shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace,
+ shaders::LinearEffect{.inputDataspace = inputDataspace,
.outputDataspace = parameters.outputDataSpace,
.undoPremultipliedAlpha = parameters.undoPremultipliedAlpha,
.fakeOutputDataspace = parameters.fakeOutputDataspace};
@@ -555,20 +586,20 @@
mat4 colorTransform = parameters.layer.colorTransform;
- colorTransform *=
- mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
- parameters.layerDimmingRatio, 1.f));
+ if (!usingLocalTonemap) {
+ colorTransform *=
+ mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+ parameters.layerDimmingRatio, 1.f));
+ }
- const auto targetBuffer = parameters.layer.source.buffer.buffer;
- const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
- return createLinearEffectShader(parameters.shader, effect, runtimeEffect,
- std::move(colorTransform), parameters.display.maxLuminance,
+ return createLinearEffectShader(shader, effect, runtimeEffect, std::move(colorTransform),
+ parameters.display.maxLuminance,
parameters.display.currentLuminanceNits,
parameters.layer.source.buffer.maxLuminanceNits,
hardwareBuffer, parameters.display.renderIntent);
}
- return parameters.shader;
+ return shader;
}
void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -683,7 +714,7 @@
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
- ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
+ SFTRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
std::lock_guard<std::mutex> lock(mRenderingMutex);
@@ -775,7 +806,7 @@
logSettings(display);
}
for (const auto& layer : layers) {
- ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
+ SFTRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
if (kPrintLayerSettings) {
logSettings(layer);
@@ -869,7 +900,7 @@
// TODO(b/182216890): Filter out empty layers earlier
if (blurRect.width() > 0 && blurRect.height() > 0) {
if (layer.backgroundBlurRadius > 0) {
- ATRACE_NAME("BackgroundBlur");
+ SFTRACE_NAME("BackgroundBlur");
auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius,
blurInput, blurRect);
@@ -882,7 +913,7 @@
canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
for (auto region : layer.blurRegions) {
if (cachedBlurs[region.blurRadius] == nullptr) {
- ATRACE_NAME("BlurRegion");
+ SFTRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
mBlurFilter->generate(context, region.blurRadius, blurInput,
blurRect);
@@ -965,7 +996,7 @@
SkPaint paint;
if (layer.source.buffer.buffer) {
- ATRACE_NAME("DrawImage");
+ SFTRACE_NAME("DrawImage");
validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
const auto& item = layer.source.buffer;
auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false);
@@ -1032,18 +1063,20 @@
toSkColorSpace(layerDataspace)));
}
- paint.setShader(createRuntimeEffectShader(
- RuntimeEffectShaderParameters{.shader = shader,
- .layer = layer,
- .display = display,
- .undoPremultipliedAlpha = !item.isOpaque &&
- item.usePremultipliedAlpha,
- .requiresLinearEffect = requiresLinearEffect,
- .layerDimmingRatio = dimInLinearSpace
- ? layerDimmingRatio
- : 1.f,
- .outputDataSpace = display.outputDataspace,
- .fakeOutputDataspace = fakeDataspace}));
+ SkRect imageBounds;
+ matrix.mapRect(&imageBounds, SkRect::Make(image->bounds()));
+
+ paint.setShader(createRuntimeEffectShader(RuntimeEffectShaderParameters{
+ .shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque && item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = dimInLinearSpace ? layerDimmingRatio : 1.f,
+ .outputDataSpace = display.outputDataspace,
+ .fakeOutputDataspace = fakeDataspace,
+ .imageBounds = imageBounds,
+ }));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
static constexpr float kDimmingThreshold = 0.9f;
@@ -1096,7 +1129,7 @@
paint.setColorFilter(SkColorFilters::Matrix(colorMatrix));
}
} else {
- ATRACE_NAME("DrawColor");
+ SFTRACE_NAME("DrawColor");
const auto color = layer.source.solidColor;
sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
.fG = color.g,
@@ -1111,7 +1144,8 @@
.requiresLinearEffect = requiresLinearEffect,
.layerDimmingRatio = layerDimmingRatio,
.outputDataSpace = display.outputDataspace,
- .fakeOutputDataspace = fakeDataspace}));
+ .fakeOutputDataspace = fakeDataspace,
+ .imageBounds = SkRect::MakeEmpty()}));
}
if (layer.disableBlending) {
@@ -1152,7 +1186,7 @@
canvas->drawRect(bounds.rect(), paint);
}
if (kGaneshFlushAfterEveryLayer) {
- ATRACE_NAME("flush surface");
+ SFTRACE_NAME("flush surface");
// No-op in Graphite. If "flushing" Skia's drawing commands after each layer is desired
// in Graphite, then a graphite::Recording would need to be snapped and tracked for each
// layer, which is likely possible but adds non-trivial complexity (in both bookkeeping
@@ -1166,11 +1200,48 @@
LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
+ trace(drawFence);
+ resultPromise->set_value(std::move(drawFence));
+}
- if (ATRACE_ENABLED()) {
- static gui::FenceMonitor sMonitor("RE Completion");
- sMonitor.queueFence(drawFence);
- }
+void SkiaRenderEngine::drawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto context = getActiveContext();
+ auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true);
+ sk_sp<SkSurface> dstSurface =
+ surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR);
+
+ waitFence(context, sdrFence);
+ const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false);
+ const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
+ const auto sdrShader =
+ sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ nullptr);
+ waitFence(context, hdrFence);
+ const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false);
+ const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
+ const auto hdrShader =
+ hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ nullptr);
+
+ static GainmapFactory kGainmapFactory;
+ const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio);
+
+ const auto canvas = dstSurface->getCanvas();
+ SkPaint paint;
+ paint.setShader(gainmapShader);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas->drawPaint(paint);
+
+ auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
+ trace(drawFence);
resultPromise->set_value(std::move(drawFence));
}
@@ -1185,7 +1256,7 @@
void SkiaRenderEngine::drawShadow(SkCanvas* canvas,
const SkRRect& casterRRect,
const ShadowSettings& settings) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const float casterZ = settings.length / 2.0f;
const auto flags =
settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index c8f9241..b5f8898 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -18,11 +18,12 @@
#define SF_SKIARENDERENGINE_H_
#include <renderengine/RenderEngine.h>
-#include <sys/types.h>
-#include <GrBackendSemaphore.h>
-#include <SkSurface.h>
#include <android-base/thread_annotations.h>
+#include <include/core/SkImageInfo.h>
+#include <include/core/SkSurface.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
@@ -32,12 +33,11 @@
#include <unordered_map>
#include "AutoBackendTexture.h"
-#include "GrContextOptions.h"
-#include "SkImageInfo.h"
#include "android-base/macros.h"
#include "compat/SkiaGpuContext.h"
#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
+#include "filters/EdgeExtensionShaderFactory.h"
#include "filters/LinearEffect.h"
#include "filters/StretchShaderFactory.h"
@@ -142,6 +142,13 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override final;
+ void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) override final;
void dump(std::string& result) override final;
@@ -156,6 +163,7 @@
float layerDimmingRatio;
const ui::Dataspace outputDataSpace;
const ui::Dataspace fakeOutputDataspace;
+ const SkRect& imageBounds;
};
sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
@@ -175,6 +183,7 @@
AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
+ EdgeExtensionShaderFactory mEdgeExtensionShaderFactory;
sp<Fence> mLastDrawFence;
BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index bd50107..677a2b6 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -24,18 +24,16 @@
#include "GaneshVkRenderEngine.h"
#include "compat/SkiaGpuContext.h"
-#include <GrBackendSemaphore.h>
-#include <GrContextOptions.h>
-#include <GrDirectContext.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
-#include <vk/GrVkExtensions.h>
-#include <vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include <android-base/stringprintf.h>
-#include <gui/TraceUtils.h>
+#include <common/trace.h>
#include <sync/sync.h>
-#include <utils/Trace.h>
#include <memory>
#include <string>
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 0a2f9b2..d2bb3d5 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -17,8 +17,6 @@
#ifndef SF_SKIAVKRENDERENGINE_H_
#define SF_SKIAVKRENDERENGINE_H_
-#include <vk/GrVkBackendContext.h>
-
#include "SkiaRenderEngine.h"
#include "VulkanInterface.h"
#include "compat/SkiaGpuContext.h"
diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp
index 5e756b0..37b69f6 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -32,21 +32,8 @@
namespace renderengine {
namespace skia {
-GrVkBackendContext VulkanInterface::getGaneshBackendContext() {
- GrVkBackendContext backendContext;
- backendContext.fInstance = mInstance;
- backendContext.fPhysicalDevice = mPhysicalDevice;
- backendContext.fDevice = mDevice;
- backendContext.fQueue = mQueue;
- backendContext.fGraphicsQueueIndex = mQueueIndex;
- backendContext.fMaxAPIVersion = mApiVersion;
- backendContext.fVkExtensions = &mGrExtensions;
- backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2;
- backendContext.fGetProc = mGrGetProc;
- backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo;
- backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived
- backendContext.fDeviceLostProc = onVkDeviceFault;
- return backendContext;
+VulkanBackendContext VulkanInterface::getGaneshBackendContext() {
+ return this->getGraphiteBackendContext();
};
VulkanBackendContext VulkanInterface::getGraphiteBackendContext() {
@@ -57,7 +44,7 @@
backendContext.fQueue = mQueue;
backendContext.fGraphicsQueueIndex = mQueueIndex;
backendContext.fMaxAPIVersion = mApiVersion;
- backendContext.fVkExtensions = &mGrExtensions;
+ backendContext.fVkExtensions = &mVulkanExtensions;
backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2;
backendContext.fGetProc = mGrGetProc;
backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo;
@@ -429,11 +416,11 @@
mDeviceExtensionNames.push_back(devExt.extensionName);
}
- mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(),
- enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(),
- enabledDeviceExtensionNames.data());
+ mVulkanExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(),
+ enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(),
+ enabledDeviceExtensionNames.data());
- if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ if (!mVulkanExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
BAIL("Vulkan driver doesn't support external semaphore fd");
}
@@ -458,7 +445,7 @@
tailPnext = &mProtectedMemoryFeatures->pNext;
}
- if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
+ if (mVulkanExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT;
mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
mDeviceFaultFeatures->pNext = nullptr;
@@ -484,7 +471,7 @@
queuePriority,
};
- if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
+ if (mVulkanExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
queueNextPtr = &queuePriorityCreateInfo;
}
@@ -606,7 +593,7 @@
mQueue = VK_NULL_HANDLE; // Implicitly destroyed by destroying mDevice.
mQueueIndex = 0;
mApiVersion = 0;
- mGrExtensions = skgpu::VulkanExtensions();
+ mVulkanExtensions = skgpu::VulkanExtensions();
mGrGetProc = nullptr;
mIsProtected = false;
mIsRealtimePriority = false;
diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h
index f20b002..d0fe4d1 100644
--- a/libs/renderengine/skia/VulkanInterface.h
+++ b/libs/renderengine/skia/VulkanInterface.h
@@ -16,7 +16,7 @@
#pragma once
-#include <include/gpu/vk/GrVkBackendContext.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
#include <include/gpu/vk/VulkanExtensions.h>
#include <include/gpu/vk/VulkanTypes.h>
@@ -24,10 +24,6 @@
using namespace skgpu;
-namespace skgpu {
-struct VulkanBackendContext;
-} // namespace skgpu
-
namespace android {
namespace renderengine {
namespace skia {
@@ -48,7 +44,8 @@
bool takeOwnership();
void teardown();
- GrVkBackendContext getGaneshBackendContext();
+ // TODO(b/309785258) Combine these into one now that they are the same implementation.
+ VulkanBackendContext getGaneshBackendContext();
VulkanBackendContext getGraphiteBackendContext();
VkSemaphore createExportableSemaphore();
VkSemaphore importSemaphoreFromSyncFd(int syncFd);
@@ -86,7 +83,7 @@
VkQueue mQueue = VK_NULL_HANDLE;
int mQueueIndex = 0;
uint32_t mApiVersion = 0;
- skgpu::VulkanExtensions mGrExtensions;
+ skgpu::VulkanExtensions mVulkanExtensions;
VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr;
VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr;
VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr;
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
index d246466..88282e7 100644
--- a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
@@ -21,26 +21,26 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <include/core/SkImage.h>
-#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkImageGanesh.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
-#include <include/gpu/vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include "skia/ColorSpaces.h"
#include "skia/compat/SkiaBackendTexture.h"
#include <android/hardware_buffer.h>
+#include <common/trace.h>
#include <log/log_main.h>
-#include <utils/Trace.h>
namespace android::renderengine::skia {
GaneshBackendTexture::GaneshBackendTexture(sk_sp<GrDirectContext> grContext,
AHardwareBuffer* buffer, bool isOutputBuffer)
: SkiaBackendTexture(buffer, isOutputBuffer), mGrContext(grContext) {
- ATRACE_CALL();
+ SFTRACE_CALL();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h
index 5cf8647..4337df1 100644
--- a/libs/renderengine/skia/compat/GaneshBackendTexture.h
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h
@@ -21,7 +21,7 @@
#include <include/android/GrAHardwareBufferUtils.h>
#include <include/core/SkColorSpace.h>
-#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <android-base/macros.h>
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
index b2eae00..931f843 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -19,13 +19,13 @@
#include <include/core/SkImageInfo.h>
#include <include/core/SkSurface.h>
#include <include/core/SkTraceMemoryDump.h>
-#include <include/gpu/GrDirectContext.h>
-#include <include/gpu/GrTypes.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
-#include <include/gpu/gl/GrGLInterface.h>
-#include <include/gpu/vk/GrVkBackendContext.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
#include "../AutoBackendTexture.h"
#include "GaneshBackendTexture.h"
@@ -56,10 +56,10 @@
}
std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Ganesh(
- const GrVkBackendContext& grVkBackendContext,
+ const skgpu::VulkanBackendContext& vkBackendContext,
GrContextOptions::PersistentCache& skSLCacheMonitor) {
return std::make_unique<GaneshGpuContext>(
- GrDirectContexts::MakeVulkan(grVkBackendContext, ganeshOptions(skSLCacheMonitor)));
+ GrDirectContexts::MakeVulkan(vkBackendContext, ganeshOptions(skSLCacheMonitor)));
}
GaneshGpuContext::GaneshGpuContext(sk_sp<GrDirectContext> grContext) : mGrContext(grContext) {
diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
index 3dd9ed2..a6e93ba 100644
--- a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
+++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
@@ -28,16 +28,16 @@
#include "skia/ColorSpaces.h"
#include <android/hardware_buffer.h>
+#include <common/trace.h>
#include <inttypes.h>
#include <log/log_main.h>
-#include <utils/Trace.h>
namespace android::renderengine::skia {
GraphiteBackendTexture::GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder,
AHardwareBuffer* buffer, bool isOutputBuffer)
: SkiaBackendTexture(buffer, isOutputBuffer), mRecorder(std::move(recorder)) {
- ATRACE_CALL();
+ SFTRACE_CALL();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
diff --git a/libs/renderengine/skia/compat/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h
index 09877a5..fa12624 100644
--- a/libs/renderengine/skia/compat/SkiaBackendTexture.h
+++ b/libs/renderengine/skia/compat/SkiaBackendTexture.h
@@ -18,7 +18,7 @@
#include <include/android/GrAHardwareBufferUtils.h>
#include <include/core/SkColorSpace.h>
-#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <android/hardware_buffer.h>
#include <ui/GraphicTypes.h>
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index 282dfe7..0bd8283 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -20,11 +20,10 @@
#define LOG_TAG "RenderEngine"
#include <include/core/SkSurface.h>
-#include <include/gpu/GrDirectContext.h>
-#include <include/gpu/gl/GrGLInterface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
#include <include/gpu/graphite/Context.h>
-#include <include/gpu/vk/GrVkBackendContext.h>
-#include "include/gpu/vk/VulkanBackendContext.h"
+#include <include/gpu/vk/VulkanBackendContext.h>
#include "SkiaBackendTexture.h"
@@ -52,10 +51,10 @@
GrContextOptions::PersistentCache& skSLCacheMonitor);
/**
- * grVkBackendContext must remain valid until after SkiaGpuContext is destroyed.
+ * vkBackendContext must remain valid until after SkiaGpuContext is destroyed.
*/
static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh(
- const GrVkBackendContext& grVkBackendContext,
+ const skgpu::VulkanBackendContext& vkBackendContext,
GrContextOptions::PersistentCache& skSLCacheMonitor);
// TODO: b/293371537 - Need shader / pipeline monitoring support in Graphite.
diff --git a/libs/renderengine/skia/debug/CommonPool.cpp b/libs/renderengine/skia/debug/CommonPool.cpp
index bf15300..9d7c69b 100644
--- a/libs/renderengine/skia/debug/CommonPool.cpp
+++ b/libs/renderengine/skia/debug/CommonPool.cpp
@@ -20,8 +20,8 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <common/trace.h>
#include <sys/resource.h>
-#include <utils/Trace.h>
#include <system/thread_defs.h>
#include <array>
@@ -31,7 +31,7 @@
namespace skia {
CommonPool::CommonPool() {
- ATRACE_CALL();
+ SFTRACE_CALL();
CommonPool* pool = this;
// Create 2 workers
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index e778884..e6a0e22 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -22,9 +22,9 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <log/log.h>
#include <renderengine/RenderEngine.h>
-#include <utils/Trace.h>
#include "CommonPool.h"
#include "SkCanvas.h"
@@ -48,7 +48,7 @@
}
SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
- ATRACE_CALL();
+ SFTRACE_CALL();
// If we are not running yet, set up.
if (CC_LIKELY(!mCaptureRunning)) {
@@ -86,7 +86,7 @@
}
void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
- ATRACE_CALL();
+ SFTRACE_CALL();
// Don't end anything if we are not running.
if (CC_LIKELY(!mCaptureRunning)) {
return;
@@ -102,7 +102,7 @@
}
SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) {
- ATRACE_CALL();
+ SFTRACE_CALL();
// Don't start anything if we are not running.
if (CC_LIKELY(!mCaptureRunning)) {
return surface->getCanvas();
@@ -122,7 +122,7 @@
}
uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) {
- ATRACE_CALL();
+ SFTRACE_CALL();
// Don't end anything if we are not running.
if (CC_LIKELY(!mCaptureRunning)) {
return 0;
@@ -151,7 +151,7 @@
}
void SkiaCapture::writeToFile() {
- ATRACE_CALL();
+ SFTRACE_CALL();
// Pass mMultiPic and mOpenMultiPicStream to a background thread, which will
// handle the heavyweight serialization work and destroy them.
// mOpenMultiPicStream is released to a bare pointer because keeping it in
@@ -169,7 +169,7 @@
}
bool SkiaCapture::setupMultiFrameCapture() {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, "");
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 1e0c4cf..cd1bd71 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -25,8 +25,8 @@
#include <SkString.h>
#include <SkSurface.h>
#include <SkTileMode.h>
+#include <common/trace.h>
#include <log/log.h>
-#include <utils/Trace.h>
namespace android {
namespace renderengine {
@@ -79,7 +79,7 @@
const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
sk_sp<SkImage> input) {
- ATRACE_CALL();
+ SFTRACE_CALL();
SkPaint paint;
paint.setAlphaf(blurAlpha);
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
new file mode 100644
index 0000000..4164c4b
--- /dev/null
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EdgeExtensionShaderFactory.h"
+#include <SkPoint.h>
+#include <SkRuntimeEffect.h>
+#include <SkStream.h>
+#include <SkString.h>
+#include <com_android_graphics_libgui_flags.h>
+#include "log/log_main.h"
+
+namespace android::renderengine::skia {
+
+static const SkString edgeShader = SkString(R"(
+ uniform shader uContentTexture;
+ uniform vec2 uImgSize;
+
+ // TODO(b/214232209) oobTolerance is temporary and will be removed when the scrollbar will be
+ // hidden during the animation
+ const float oobTolerance = 15;
+ const int blurRadius = 3;
+ const float blurArea = float((2 * blurRadius + 1) * (2 * blurRadius + 1));
+
+ vec4 boxBlur(vec2 p) {
+ vec4 sumColors = vec4(0);
+
+ for (int i = -blurRadius; i <= blurRadius; i++) {
+ for (int j = -blurRadius; j <= blurRadius; j++) {
+ sumColors += uContentTexture.eval(p + vec2(i, j));
+ }
+ }
+ return sumColors / blurArea;
+ }
+
+ vec4 main(vec2 coord) {
+ vec2 nearestTexturePoint = clamp(coord, vec2(0, 0), uImgSize);
+ if (coord == nearestTexturePoint) {
+ return uContentTexture.eval(coord);
+ } else {
+ vec2 samplePoint = nearestTexturePoint + oobTolerance * normalize(
+ nearestTexturePoint - coord);
+ return boxBlur(samplePoint);
+ }
+ }
+)");
+
+EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() {
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ return;
+ }
+ mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader));
+ LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(),
+ "EdgeExtensionShaderFactory compilation "
+ "failed with an unexpected error: %s",
+ mResult->errorText.c_str());
+}
+
+sk_sp<SkShader> EdgeExtensionShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
+ const LayerSettings& layer,
+ const SkRect& imageBounds) const {
+ LOG_ALWAYS_FATAL_IF(mResult == nullptr,
+ "EdgeExtensionShaderFactory did not initialize mResult. "
+ "This means that we unexpectedly applied the edge extension shader");
+
+ SkRuntimeShaderBuilder builder = SkRuntimeShaderBuilder(mResult->effect);
+
+ builder.child("uContentTexture") = inputShader;
+ if (imageBounds.isEmpty()) {
+ builder.uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(),
+ layer.geometry.boundaries.getHeight()};
+ } else {
+ builder.uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()};
+ }
+ return builder.makeShader();
+}
+} // namespace android::renderengine::skia
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
new file mode 100644
index 0000000..17c6b91
--- /dev/null
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <SkImage.h>
+#include <SkRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/EdgeExtensionEffect.h>
+
+namespace android::renderengine::skia {
+
+/**
+ * This shader is designed to prolong the texture of a surface whose bounds have been extended over
+ * the size of the texture. This shader is similar to the default clamp, but adds a blur effect and
+ * samples from close to the edge (compared to on the edge) to avoid weird artifacts when elements
+ * (in particular, scrollbars) touch the edge.
+ */
+class EdgeExtensionShaderFactory {
+public:
+ EdgeExtensionShaderFactory();
+
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader, const LayerSettings& layer,
+ const SkRect& imageBounds) const;
+
+private:
+ std::unique_ptr<const SkRuntimeEffect::Result> mResult;
+};
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/filters/GainmapFactory.cpp b/libs/renderengine/skia/filters/GainmapFactory.cpp
new file mode 100644
index 0000000..e4d4fe9
--- /dev/null
+++ b/libs/renderengine/skia/filters/GainmapFactory.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "GainmapFactory.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+namespace {
+
+sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) {
+ auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl);
+ LOG_ALWAYS_FATAL_IF(!effect, "RuntimeShader error: %s", error.c_str());
+ return effect;
+}
+
+// Please refer to https://developer.android.com/media/platform/hdr-image-format#gain_map-generation
+static const SkString kGainmapShader = SkString(R"(
+ uniform shader sdr;
+ uniform shader hdr;
+ uniform float mapMaxLog2;
+
+ const float mapMinLog2 = 0.0;
+ const float mapGamma = 1.0;
+ const float offsetSdr = 0.015625;
+ const float offsetHdr = 0.015625;
+
+ float luminance(vec3 linearColor) {
+ return 0.2126 * linearColor.r + 0.7152 * linearColor.g + 0.0722 * linearColor.b;
+ }
+
+ vec4 main(vec2 xy) {
+ float sdrY = luminance(toLinearSrgb(sdr.eval(xy).rgb));
+ float hdrY = luminance(toLinearSrgb(hdr.eval(xy).rgb));
+ float pixelGain = (hdrY + offsetHdr) / (sdrY + offsetSdr);
+ float logRecovery = (log2(pixelGain) - mapMinLog2) / (mapMaxLog2 - mapMinLog2);
+ return vec4(pow(clamp(logRecovery, 0.0, 1.0), mapGamma));
+ }
+)");
+} // namespace
+
+const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+
+GainmapFactory::GainmapFactory() : mEffect(makeEffect(kGainmapShader)) {}
+
+sk_sp<SkShader> GainmapFactory::createSkShader(const sk_sp<SkShader>& sdr,
+ const sk_sp<SkShader>& hdr, float hdrSdrRatio) {
+ SkRuntimeShaderBuilder shaderBuilder(mEffect);
+ shaderBuilder.child("sdr") = sdr;
+ shaderBuilder.child("hdr") = hdr;
+ shaderBuilder.uniform("mapMaxLog2") = std::log2(hdrSdrRatio);
+ return shaderBuilder.makeShader();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GainmapFactory.h b/libs/renderengine/skia/filters/GainmapFactory.h
new file mode 100644
index 0000000..7aea5e2
--- /dev/null
+++ b/libs/renderengine/skia/filters/GainmapFactory.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <SkRuntimeEffect.h>
+#include <SkShader.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Generates a shader for computing a gainmap, given an SDR base image and its idealized HDR
+ * rendition. The shader follows the procedure in the UltraHDR spec:
+ * https://developer.android.com/media/platform/hdr-image-format#gain_map-generation, but makes some
+ * simplifying assumptions about metadata typical for RenderEngine's usage.
+ */
+class GainmapFactory {
+public:
+ GainmapFactory();
+ // Generates the gainmap shader. The hdrSdrRatio is the max_content_boost in the UltraHDR
+ // specification.
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& sdr, const sk_sp<SkShader>& hdr,
+ float hdrSdrRatio);
+
+private:
+ sk_sp<SkRuntimeEffect> mEffect;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index c9499cb..8c52c57 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -19,18 +19,18 @@
#include "GaussianBlurFilter.h"
#include <SkBlendMode.h>
#include <SkCanvas.h>
+#include <SkImageFilters.h>
#include <SkPaint.h>
#include <SkRRect.h>
#include <SkRuntimeEffect.h>
-#include <SkImageFilters.h>
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
#include <SkTileMode.h>
+#include <common/trace.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include "include/gpu/GpuTypes.h" // from Skia
#include <log/log.h>
-#include <utils/Trace.h>
+#include "include/gpu/GpuTypes.h" // from Skia
namespace android {
namespace renderengine {
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
new file mode 100644
index 0000000..db0b133
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurDualFilter.h"
+#include <SkAlphaType.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() {
+ // A shader to sample each vertex of a unit regular heptagon
+ // plus the original fragment coordinate.
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+ uniform float in_crossFade;
+
+ const float2 STEP_0 = float2( 1.0, 0.0);
+ const float2 STEP_1 = float2( 0.623489802, 0.781831482);
+ const float2 STEP_2 = float2(-0.222520934, 0.974927912);
+ const float2 STEP_3 = float2(-0.900968868, 0.433883739);
+ const float2 STEP_4 = float2( 0.900968868, -0.433883739);
+ const float2 STEP_5 = float2(-0.222520934, -0.974927912);
+ const float2 STEP_6 = float2(-0.623489802, -0.781831482);
+
+ half4 main(float2 xy) {
+ half3 c = child.eval(xy).rgb;
+
+ c += child.eval(xy + STEP_0 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_1 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_2 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_3 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_4 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_5 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_6 * in_blurOffset).rgb;
+
+ return half4(c * 0.125 * in_crossFade, in_crossFade);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ LOG_ALWAYS_FATAL_IF(!blurEffect, "RuntimeShader error: %s", error.c_str());
+ mBlurEffect = std::move(blurEffect);
+}
+
+static sk_sp<SkSurface> makeSurface(SkiaGpuContext* context, const SkRect& origRect, int scale) {
+ SkImageInfo scaledInfo =
+ SkImageInfo::MakeN32Premul(ceil(static_cast<float>(origRect.width()) / scale),
+ ceil(static_cast<float>(origRect.height()) / scale));
+ return context->createRenderTarget(scaledInfo);
+}
+
+void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface,
+ const sk_sp<SkImage>& readImage, const float radius,
+ const float alpha) const {
+ const float scale = static_cast<float>(drawSurface->width()) / readImage->width();
+ SkMatrix blurMatrix = SkMatrix::Scale(scale, scale);
+ blurInto(drawSurface,
+ readImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
+ blurMatrix),
+ readImage->width() / static_cast<float>(drawSurface->width()), radius, alpha);
+}
+
+void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input,
+ const float inverseScale, const float radius,
+ const float alpha) const {
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") = std::move(input);
+ blurBuilder.uniform("in_inverseScale") = inverseScale;
+ blurBuilder.uniform("in_blurOffset") = radius;
+ blurBuilder.uniform("in_crossFade") = alpha;
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr));
+ paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
+ drawSurface->getCanvas()->drawPaint(paint);
+}
+
+sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input,
+ const SkRect& blurRect) const {
+ // Apply a conversion factor of (1 / sqrt(3)) to match Skia's built-in blur as used by
+ // RenderEffect. See the comment in SkBlurMask.cpp for reasoning behind this.
+ const float radius = blurRadius * 0.57735f;
+
+ // Use a variable number of blur passes depending on the radius. The non-integer part of this
+ // calculation is used to mix the final pass into the second-last with an alpha blend.
+ constexpr int kMaxSurfaces = 4;
+ const float filterDepth =
+ std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale)));
+ const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));
+
+ // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale.
+ sk_sp<SkSurface> surfaces[kMaxSurfaces] =
+ {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
+ filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
+ filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
+ filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
+
+ // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600.
+ static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f};
+
+ // Kawase is an approximation of Gaussian, but behaves differently because it is made up of many
+ // simpler blurs. A transformation is required to approximate the same effect as Gaussian.
+ float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f);
+ for (int i = 0; i < filterPasses; i++) {
+ const float alpha = std::min(1.0f, filterDepth - i);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f);
+ }
+ // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets.
+ const float step = M_SQRT1_2 *
+ sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR));
+
+ // Start by downscaling and doing the first blur pass.
+ {
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case one may expect Translate(blurRect.fLeft, blurRect.fTop) * Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+ const auto sourceShader =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
+ blurMatrix);
+ blurInto(surfaces[0], std::move(sourceShader), kInputScale, kWeights[0] * step, 1.0f);
+ }
+ // Next the remaining downscale blur passes.
+ for (int i = 0; i < filterPasses; i++) {
+ blurInto(surfaces[i + 1], surfaces[i]->makeImageSnapshot(), kWeights[1 + i] * step, 1.0f);
+ }
+ // Finally blur+upscale back to our original size.
+ for (int i = filterPasses - 1; i >= 0; i--) {
+ blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step,
+ std::min(1.0f, filterDepth - i));
+ }
+ return surfaces[0]->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
new file mode 100644
index 0000000..6f4adbf
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+#include "BlurFilter.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur with dual-filtering passes, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_slides.pdf
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurDualFilter : public BlurFilter {
+public:
+ explicit KawaseBlurDualFilter();
+ virtual ~KawaseBlurDualFilter() {}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+
+ void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkImage>& readImage,
+ const float radius, const float alpha) const;
+
+ void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkShader> input,
+ const float inverseScale, const float radius, const float alpha) const;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index 7a070d7..defaf6e 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -29,10 +29,10 @@
#include <SkString.h>
#include <SkSurface.h>
#include <SkTileMode.h>
+#include <common/trace.h>
#include <include/gpu/GpuTypes.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <log/log.h>
-#include <utils/Trace.h>
namespace android {
namespace renderengine {
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index f7dcd3a..3bc3564 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,9 +19,9 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <SkString.h>
+#include <common/trace.h>
#include <log/log.h>
#include <shaders/shaders.h>
-#include <utils/Trace.h>
#include <math/mat4.h>
@@ -30,7 +30,7 @@
namespace skia {
sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) {
- ATRACE_CALL();
+ SFTRACE_CALL();
SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
@@ -45,7 +45,7 @@
sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance,
float currentDisplayLuminanceNits, float maxLuminance, AHardwareBuffer* buffer,
aidl::android::hardware::graphics::composer3::RenderIntent renderIntent) {
- ATRACE_CALL();
+ SFTRACE_CALL();
SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
effectBuilder.child("child") = shader;
diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp
index 7d8b8a5..b099bcf 100644
--- a/libs/renderengine/skia/filters/MouriMap.cpp
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -35,7 +35,7 @@
float maximum = 0.0;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
- float3 linear = toLinearSrgb(bitmap.eval(xy * 16 + vec2(x, y)).rgb) * hdrSdrRatio;
+ float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * hdrSdrRatio;
float maxRGB = max(linear.r, max(linear.g, linear.b));
maximum = max(maximum, log2(max(maxRGB, 1.0)));
}
@@ -49,7 +49,7 @@
float maximum = 0.0;
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
- maximum = max(maximum, bitmap.eval(xy * 8 + vec2(x, y)).r);
+ maximum = max(maximum, bitmap.eval((xy - 0.5) * 8 + 0.5 + vec2(x, y)).r);
}
}
return float4(float3(maximum), 1.0);
@@ -67,7 +67,7 @@
float result = 0.0;
for (int y = -2; y <= 2; y++) {
for (int x = -2; x <= 2; x++) {
- result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r;
+ result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r;
}
}
return float4(float3(exp2(result)), 1.0);
@@ -78,19 +78,21 @@
uniform shader lux;
uniform float scaleFactor;
uniform float hdrSdrRatio;
+ uniform float targetHdrSdrRatio;
vec4 main(vec2 xy) {
float localMax = lux.eval(xy * scaleFactor).r;
float4 rgba = image.eval(xy);
float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio;
- if (localMax <= 1.0) {
- return float4(fromLinearSrgb(linear), 1.0);
+ if (localMax <= targetHdrSdrRatio) {
+ return float4(fromLinearSrgb(linear), rgba.a);
}
float maxRGB = max(linear.r, max(linear.g, linear.b));
localMax = max(localMax, maxRGB);
- float gain = (1 + maxRGB / (localMax * localMax)) / (1 + maxRGB);
- return float4(fromLinearSrgb(linear * gain), 1.0);
+ float gain = (1 + maxRGB * (targetHdrSdrRatio / (localMax * localMax)))
+ / (1 + maxRGB / targetHdrSdrRatio);
+ return float4(fromLinearSrgb(linear * gain), rgba.a);
}
)");
@@ -114,10 +116,10 @@
mTonemap(makeEffect(kTonemap)) {}
sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input,
- float hdrSdrRatio) {
+ float hdrSdrRatio, float targetHdrSdrRatio) {
auto downchunked = downchunk(context, input, hdrSdrRatio);
auto localLux = blur(context, downchunked.get());
- return tonemap(input, localLux.get(), hdrSdrRatio);
+ return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio);
}
sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
@@ -166,8 +168,8 @@
LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__);
return makeImage(blurSurface.get(), blurBuilder);
}
-sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux,
- float hdrSdrRatio) const {
+sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio,
+ float targetHdrSdrRatio) const {
static constexpr float kScaleFactor = 1.0f / 128.0f;
SkRuntimeShaderBuilder tonemapBuilder(mTonemap);
tonemapBuilder.child("image") = input;
@@ -176,8 +178,9 @@
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
tonemapBuilder.uniform("scaleFactor") = kScaleFactor;
tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio;
return tonemapBuilder.makeShader();
}
} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h
index 3c0df8a..9ba2b6f 100644
--- a/libs/renderengine/skia/filters/MouriMap.h
+++ b/libs/renderengine/skia/filters/MouriMap.h
@@ -64,13 +64,16 @@
// Apply the MouriMap tonemmaping operator to the input.
// The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger
// then 1.0 means that there is headroom above the SDR region.
- sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio);
+ // Similarly, the target HDR/SDR ratio describes the luminance range of the output.
+ sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio,
+ float targetHdrSdrRatio);
private:
sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
float hdrSdrRatio) const;
sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const;
- sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const;
+ sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio,
+ float targetHdrSdrRatio) const;
const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16;
const sk_sp<SkRuntimeEffect> mChunk8x8;
const sk_sp<SkRuntimeEffect> mBlur;
@@ -78,4 +81,4 @@
};
} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 0783714..7fbbf49 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -66,5 +66,6 @@
"libutils",
"server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libtracing_perfetto",
],
}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index a8a9823..b5cc65f 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -34,9 +34,12 @@
#include <ui/ColorSpace.h>
#include <ui/PixelFormat.h>
+#include <algorithm>
#include <chrono>
#include <condition_variable>
+#include <filesystem>
#include <fstream>
+#include <system_error>
#include "../skia/SkiaGLRenderEngine.h"
#include "../skia/SkiaVkRenderEngine.h"
@@ -259,22 +262,51 @@
~RenderEngineTest() {
if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
- writeBufferToFile("/data/texture_out_");
+ writeBufferToFile("/data/local/tmp/RenderEngineTest/");
}
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGI("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
- void writeBufferToFile(const char* basename) {
- std::string filename(basename);
- filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
- filename.append(".ppm");
- std::ofstream file(filename.c_str(), std::ios::binary);
+ // If called during e.g.
+ // `PerRenderEngineType/RenderEngineTest#drawLayers_fillBufferCheckersRotate90_colorSource/0`
+ // with a directory of `/data/local/tmp/RenderEngineTest`, then mBuffer will be dumped to
+ // `/data/local/tmp/RenderEngineTest/drawLayers_fillBufferCheckersRotate90_colorSource-0.ppm`
+ //
+ // Note: if `directory` does not exist, then its full path will be recursively created with 777
+ // permissions. If `directory` already exists but does not grant the executing user write
+ // permissions, then saving the buffer will fail.
+ //
+ // Since this is test-only code, no security considerations are made.
+ void writeBufferToFile(const filesystem::path& directory) {
+ const auto currentTestInfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ LOG_ALWAYS_FATAL_IF(!currentTestInfo,
+ "writeBufferToFile must be called during execution of a test");
+
+ std::string fileName(currentTestInfo->name());
+ // Test names may include the RenderEngine variant separated by '/', which would separate
+ // the file name into a subdirectory if not corrected.
+ std::replace(fileName.begin(), fileName.end(), '/', '-');
+ fileName.append(".ppm");
+
+ std::error_code err;
+ filesystem::create_directories(directory, err);
+ if (err.value()) {
+ ALOGE("Unable to create directory %s for writing %s (%d: %s)", directory.c_str(),
+ fileName.c_str(), err.value(), err.message().c_str());
+ return;
+ }
+
+ // Append operator ("/") ensures exactly one "/" directly before the argument.
+ const filesystem::path filePath = directory / fileName;
+ std::ofstream file(filePath.c_str(), std::ios::binary);
if (!file.is_open()) {
- ALOGE("Unable to open file: %s", filename.c_str());
- ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
- "surfaceflinger to write debug images");
+ ALOGE("Unable to open file: %s", filePath.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable surfaceflinger to "
+ "write debug images, or the %s directory might not give the executing user write "
+ "permission",
+ directory.c_str());
return;
}
@@ -304,6 +336,7 @@
}
}
file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+ ALOGI("Image of incorrect output written to %s", filePath.c_str());
mBuffer->getBuffer()->unlock();
}
@@ -3147,6 +3180,214 @@
expectBufferColor(Rect(0, 0, 1, 1), 0, 70, 0, 255);
}
+TEST_P(RenderEngineTest, localTonemap_preservesFullscreenSdr) {
+ if (!GetParam()->apiSupported()) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ mBuffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, mBuffer->getBuffer()->initCheck());
+
+ const auto whiteBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(51, 51, 51, 255));
+
+ const auto rect = Rect(0, 0, 1, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .outputDataspace = ui::Dataspace::SRGB,
+ .targetLuminanceNits = 40,
+ .tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local,
+ };
+
+ const renderengine::LayerSettings whiteLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = whiteBuffer,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR,
+ .whitePointNits = 200,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{whiteLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(0, 0, 1, 1), 255, 255, 255, 255);
+}
+
+TEST_P(RenderEngineTest, localTonemap_preservesFarawaySdrRegions) {
+ if (!GetParam()->apiSupported()) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ const auto blockWidth = 256;
+ const auto width = blockWidth * 4;
+
+ const auto buffer = allocateSourceBuffer(width, 1);
+
+ mBuffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(width, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+
+ {
+ uint8_t* pixels;
+ buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ uint8_t* dst = pixels;
+ for (uint32_t i = 0; i < width; i++) {
+ uint8_t value = 0;
+ if (i < blockWidth) {
+ value = 51;
+ } else if (i >= blockWidth * 3) {
+ value = 255;
+ }
+ dst[0] = value;
+ dst[1] = value;
+ dst[2] = value;
+ dst[3] = 255;
+ dst += 4;
+ }
+ buffer->getBuffer()->unlock();
+ }
+
+ const auto rect = Rect(0, 0, width, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = 40,
+ .tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local,
+ };
+
+ const renderengine::LayerSettings whiteLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = buffer,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR,
+ .whitePointNits = 200,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{whiteLayer};
+ invokeDraw(display, layers);
+
+ // SDR regions are boosted to preserve SDR detail.
+ expectBufferColor(Rect(0, 0, blockWidth, 1), 255, 255, 255, 255);
+ expectBufferColor(Rect(blockWidth, 0, blockWidth * 2, 1), 0, 0, 0, 255);
+ expectBufferColor(Rect(blockWidth * 2, 0, blockWidth * 3, 1), 0, 0, 0, 255);
+ expectBufferColor(Rect(blockWidth * 3, 0, blockWidth * 4, 1), 255, 255, 255, 255);
+}
+
+TEST_P(RenderEngineTest, localTonemap_tonemapsNearbySdrRegions) {
+ if (!GetParam()->apiSupported()) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ const auto blockWidth = 2;
+ const auto width = blockWidth * 2;
+
+ mBuffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(width, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+
+ const auto buffer = allocateSourceBuffer(width, 1);
+
+ {
+ uint8_t* pixels;
+ buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ uint8_t* dst = pixels;
+ for (uint32_t i = 0; i < width; i++) {
+ uint8_t value = 0;
+ if (i < blockWidth) {
+ value = 51;
+ } else if (i >= blockWidth) {
+ value = 255;
+ }
+ dst[0] = value;
+ dst[1] = value;
+ dst[2] = value;
+ dst[3] = 255;
+ dst += 4;
+ }
+ buffer->getBuffer()->unlock();
+ }
+
+ const auto rect = Rect(0, 0, width, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = 40,
+ .tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local,
+ };
+
+ const renderengine::LayerSettings whiteLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = buffer,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR,
+ .whitePointNits = 200,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{whiteLayer};
+ invokeDraw(display, layers);
+
+ // SDR regions remain "dimmed", but preserve detail with a roll-off curve.
+ expectBufferColor(Rect(0, 0, blockWidth, 1), 132, 132, 132, 255, 2);
+ // HDR regions are not dimmed.
+ expectBufferColor(Rect(blockWidth, 0, blockWidth * 2, 1), 255, 255, 255, 255);
+}
+
TEST_P(RenderEngineTest, primeShaderCache) {
// TODO: b/331447071 - Fix in Graphite and re-enable.
if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index d27c151..c187f93 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -23,9 +23,9 @@
#include <future>
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <private/gui/SyncFeatures.h>
#include <processgroup/processgroup.h>
-#include <utils/Trace.h>
using namespace std::chrono_literals;
@@ -39,7 +39,7 @@
RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory)
: RenderEngine(Threaded::YES) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard lockThread(mThreadMutex);
mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
@@ -76,7 +76,7 @@
// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
ALOGW("Failed to set render-engine task profile!");
@@ -133,13 +133,13 @@
std::future<void> RenderEngineThreaded::primeCache(PrimeCacheConfig config) {
const auto resultPromise = std::make_shared<std::promise<void>>();
std::future<void> resultFuture = resultPromise->get_future();
- ATRACE_CALL();
+ SFTRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([resultPromise, config](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::primeCache");
+ SFTRACE_NAME("REThreaded::primeCache");
if (setSchedFifo(false) != NO_ERROR) {
ALOGW("Couldn't set SCHED_OTHER for primeCache");
}
@@ -163,7 +163,7 @@
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::dump");
+ SFTRACE_NAME("REThreaded::dump");
std::string localResult = result;
instance.dump(localResult);
resultPromise.set_value(std::move(localResult));
@@ -176,13 +176,13 @@
void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
- ATRACE_CALL();
+ SFTRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
+ SFTRACE_NAME("REThreaded::mapExternalTextureBuffer");
instance.mapExternalTextureBuffer(buffer, isRenderable);
});
}
@@ -190,14 +190,14 @@
}
void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
- ATRACE_CALL();
+ SFTRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push(
[=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
- ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ SFTRACE_NAME("REThreaded::unmapExternalTextureBuffer");
instance.unmapExternalTextureBuffer(std::move(buffer));
});
}
@@ -229,7 +229,7 @@
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::cleanupPostRender");
+ SFTRACE_NAME("REThreaded::cleanupPostRender");
instance.cleanupPostRender();
});
mNeedsPostRenderCleanup = false;
@@ -249,10 +249,20 @@
return;
}
+void RenderEngineThreaded::drawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ resultPromise->set_value(Fence::NO_FENCE);
+ return;
+}
+
ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
int fd = bufferFence.release();
@@ -261,8 +271,8 @@
mNeedsPostRenderCleanup = true;
mFunctionCalls.push(
[resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::drawLayers");
- instance.updateProtectedContext(layers, buffer);
+ SFTRACE_NAME("REThreaded::drawLayers");
+ instance.updateProtectedContext(layers, {buffer.get()});
instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
base::unique_fd(fd));
});
@@ -271,13 +281,37 @@
return resultFuture;
}
+ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap(
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ SFTRACE_CALL();
+ const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
+ std::future<FenceResult> resultFuture = resultPromise->get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mNeedsPostRenderCleanup = true;
+ mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr,
+ hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace,
+ gainmap](renderengine::RenderEngine& instance) mutable {
+ SFTRACE_NAME("REThreaded::drawGainmap");
+ instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
+ instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
+ std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture;
+}
+
int RenderEngineThreaded::getContextPriority() {
std::promise<int> resultPromise;
std::future<int> resultFuture = resultPromise.get_future();
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::getContextPriority");
+ SFTRACE_NAME("REThreaded::getContextPriority");
int priority = instance.getContextPriority();
resultPromise.set_value(priority);
});
@@ -297,7 +331,7 @@
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
+ SFTRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
instance.onActiveDisplaySizeChanged(size);
});
}
@@ -324,7 +358,7 @@
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([tracingEnabled](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::setEnableTracing");
+ SFTRACE_NAME("REThreaded::setEnableTracing");
instance.setEnableTracing(tracingEnabled);
});
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d4997d6..cb6e924 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -55,6 +55,12 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override;
+ ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
@@ -71,6 +77,13 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override;
+ void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 7fa47b4..659666d 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -63,6 +63,8 @@
"libhardware",
"libpermission",
"android.companion.virtual.virtualdevice_aidl-cpp",
+ "libaconfig_storage_read_api_cc",
+ "server_configurable_flags",
],
static_libs: [
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 4438d45..bec9255 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -15,31 +15,41 @@
*/
#define LOG_TAG "Sensors"
-
-#include <sensor/SensorEventQueue.h>
-
-#include <algorithm>
-#include <sys/socket.h>
-
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-
-#include <sensor/Sensor.h>
-#include <sensor/BitTube.h>
-#include <sensor/ISensorEventConnection.h>
+#define ATRACE_TAG ATRACE_TAG_SYSTEM_SERVER
#include <android/sensor.h>
+#include <com_android_hardware_libsensor_flags.h>
+#include <cutils/trace.h>
#include <hardware/sensors-base.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
+#include <sensor/SensorManager.h>
+#include <sys/socket.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <string>
using std::min;
+namespace libsensor_flags = com::android::hardware::libsensor::flags;
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
-SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
- : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0),
- mNumAcksToSend(0) {
+SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection,
+ SensorManager& sensorManager, String8 packageName)
+ : mSensorEventConnection(connection),
+ mRecBuffer(nullptr),
+ mSensorManager(sensorManager),
+ mPackageName(packageName),
+ mAvailable(0),
+ mConsumed(0),
+ mNumAcksToSend(0) {
mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
}
@@ -65,8 +75,8 @@
ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
if (mAvailable == 0) {
- ssize_t err = BitTube::recvObjects(mSensorChannel,
- mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
+ ssize_t err =
+ BitTube::recvObjects(mSensorChannel, mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
if (err < 0) {
return err;
}
@@ -75,6 +85,23 @@
}
size_t count = min(numEvents, mAvailable);
memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
+
+ if (CC_UNLIKELY(ATRACE_ENABLED()) &&
+ libsensor_flags::sensor_event_queue_report_sensor_usage_in_tracing()) {
+ for (size_t i = 0; i < count; i++) {
+ std::optional<std::string_view> sensorName =
+ mSensorManager.getSensorNameByHandle(events->sensor);
+ if (sensorName.has_value()) {
+ char buffer[UINT8_MAX];
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ std::snprintf(buffer, sizeof(buffer),
+ "Sensor event from %s to %s PID: %d (%zu/%zu)",
+ sensorName.value().data(), mPackageName.c_str(), pid, i, count);
+ ATRACE_INSTANT_FOR_TRACK(LOG_TAG, buffer);
+ }
+ }
+ }
mAvailable -= count;
mConsumed += count;
return static_cast<ssize_t>(count);
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 9411e20..7b4a86c 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -38,6 +38,7 @@
#include <sensor/SensorEventQueue.h>
#include <com_android_hardware_libsensor_flags.h>
+namespace libsensor_flags = com::android::hardware::libsensor::flags;
// ----------------------------------------------------------------------------
namespace android {
@@ -72,12 +73,25 @@
return deviceId;
}
}
- } else {
- ALOGW("Cannot get virtualdevice_native service");
}
return DEVICE_ID_DEFAULT;
}
+bool findSensorNameInList(int32_t handle, const Vector<Sensor>& sensorList,
+ std::string* outString) {
+ for (auto& sensor : sensorList) {
+ if (sensor.getHandle() == handle) {
+ std::ostringstream oss;
+ oss << sensor.getStringType() << ":" << sensor.getName();
+ if (outString) {
+ *outString = oss.str();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
Mutex SensorManager::sLock;
@@ -355,6 +369,25 @@
return nullptr;
}
+std::optional<std::string_view> SensorManager::getSensorNameByHandle(int32_t handle) {
+ std::lock_guard<std::mutex> lock(mSensorHandleToNameMutex);
+ auto iterator = mSensorHandleToName.find(handle);
+ if (iterator != mSensorHandleToName.end()) {
+ return iterator->second;
+ }
+
+ std::string sensorName;
+ if (!findSensorNameInList(handle, mSensors, &sensorName) &&
+ !findSensorNameInList(handle, mDynamicSensors, &sensorName)) {
+ ALOGW("Cannot find sensor with handle %d", handle);
+ return std::nullopt;
+ }
+
+ mSensorHandleToName[handle] = std::move(sensorName);
+
+ return mSensorHandleToName[handle];
+}
+
sp<SensorEventQueue> SensorManager::createEventQueue(
String8 packageName, int mode, String16 attributionTag) {
sp<SensorEventQueue> queue;
@@ -368,7 +401,7 @@
ALOGE("createEventQueue: connection is NULL.");
return nullptr;
}
- queue = new SensorEventQueue(connection);
+ queue = new SensorEventQueue(connection, *this, packageName);
break;
}
return queue;
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 8c3fde0..d31def7 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -20,9 +20,10 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
#include <sensor/BitTube.h>
@@ -42,6 +43,7 @@
// ----------------------------------------------------------------------------
class ISensorEventConnection;
+class SensorManager;
class Sensor;
class Looper;
@@ -65,7 +67,8 @@
// Default sensor sample period
static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
- explicit SensorEventQueue(const sp<ISensorEventConnection>& connection);
+ explicit SensorEventQueue(const sp<ISensorEventConnection>& connection,
+ SensorManager& sensorManager, String8 packageName);
virtual ~SensorEventQueue();
virtual void onFirstRef();
@@ -107,6 +110,8 @@
mutable Mutex mLock;
mutable sp<Looper> mLooper;
ASensorEvent* mRecBuffer;
+ SensorManager& mSensorManager;
+ String8 mPackageName;
size_t mAvailable;
size_t mConsumed;
uint32_t mNumAcksToSend;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 49f050a..8d7237d 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -17,22 +17,20 @@
#ifndef ANDROID_GUI_SENSOR_MANAGER_H
#define ANDROID_GUI_SENSOR_MANAGER_H
-#include <map>
-#include <unordered_map>
-
-#include <stdint.h>
-#include <sys/types.h>
-
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-
+#include <sensor/SensorEventQueue.h>
+#include <stdint.h>
+#include <sys/types.h>
#include <utils/Errors.h>
+#include <utils/String8.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
-#include <utils/String8.h>
-#include <sensor/SensorEventQueue.h>
+#include <map>
+#include <string>
+#include <unordered_map>
// ----------------------------------------------------------------------------
// Concrete types for the NDK
@@ -66,6 +64,7 @@
sp<SensorEventQueue> createEventQueue(
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
bool isDataInjectionEnabled();
+ std::optional<std::string_view> getSensorNameByHandle(int32_t handle);
bool isReplayDataInjectionEnabled();
bool isHalBypassReplayDataInjectionEnabled();
int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
@@ -97,6 +96,9 @@
const String16 mOpPackageName;
const int mDeviceId;
std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+
+ std::mutex mSensorHandleToNameMutex;
+ std::unordered_map<int32_t, std::string> mSensorHandleToName;
int32_t mDirectConnectionHandle;
};
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index 3a4c869..b5c56c5 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -40,6 +40,7 @@
],
shared_libs: [
+ "libbase",
"libcutils",
"libperfetto_c",
"android.os.flags-aconfig-cc-host",
diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h
index 2c1c2a4..59c43d6 100644
--- a/libs/tracing_perfetto/include/tracing_perfetto.h
+++ b/libs/tracing_perfetto/include/tracing_perfetto.h
@@ -14,40 +14,40 @@
* limitations under the License.
*/
-#ifndef TRACING_PERFETTO_H
-#define TRACING_PERFETTO_H
+#pragma once
#include <stdint.h>
-#include "trace_result.h"
-
namespace tracing_perfetto {
void registerWithPerfetto(bool test = false);
-Result traceBegin(uint64_t category, const char* name);
+void traceBegin(uint64_t category, const char* name);
-Result traceEnd(uint64_t category);
+void traceEnd(uint64_t category);
-Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
+void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
-Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
+void traceFormatBegin(uint64_t category, const char* fmt, ...);
-Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
+
+void traceAsyncBeginForTrack(uint64_t category, const char* name,
const char* trackName, int32_t cookie);
-Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+void traceAsyncEndForTrack(uint64_t category, const char* trackName,
int32_t cookie);
-Result traceInstant(uint64_t category, const char* name);
+void traceInstant(uint64_t category, const char* name);
-Result traceInstantForTrack(uint64_t category, const char* trackName,
+void traceFormatInstant(uint64_t category, const char* fmt, ...);
+
+void traceInstantForTrack(uint64_t category, const char* trackName,
const char* name);
-Result traceCounter(uint64_t category, const char* name, int64_t value);
+void traceCounter(uint64_t category, const char* name, int64_t value);
+
+void traceCounter32(uint64_t category, const char* name, int32_t value);
bool isTagEnabled(uint64_t category);
-
} // namespace tracing_perfetto
-
-#endif // TRACING_PERFETTO_H
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
index a35b0e0..d203467 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -26,6 +26,7 @@
static_libs: [
"libflagtest",
"libgmock",
+ "perfetto_trace_protos",
],
cflags: [
"-Wall",
@@ -35,6 +36,8 @@
"android.os.flags-aconfig-cc-host",
"libbase",
"libperfetto_c",
+ "liblog",
+ "libprotobuf-cpp-lite",
"libtracing_perfetto",
],
srcs: [
diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
index 7716b9a..e9fee2e 100644
--- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
+++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
@@ -16,10 +16,10 @@
#include "tracing_perfetto.h"
-#include <thread>
-
#include <android_os.h>
#include <flag_macros.h>
+#include <thread>
+#include <unistd.h>
#include "gtest/gtest.h"
#include "perfetto/public/abi/data_source_abi.h"
@@ -45,67 +45,182 @@
#include "trace_categories.h"
#include "utils.h"
+#include "protos/perfetto/trace/trace.pb.h"
+#include "protos/perfetto/trace/trace_packet.pb.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
+
+#include <fstream>
+#include <iterator>
namespace tracing_perfetto {
-using ::perfetto::shlib::test_utils::AllFieldsWithId;
-using ::perfetto::shlib::test_utils::FieldView;
-using ::perfetto::shlib::test_utils::IdFieldView;
-using ::perfetto::shlib::test_utils::MsgField;
-using ::perfetto::shlib::test_utils::PbField;
-using ::perfetto::shlib::test_utils::StringField;
+using ::perfetto::protos::Trace;
+using ::perfetto::protos::TracePacket;
+using ::perfetto::protos::EventCategory;
+using ::perfetto::protos::EventName;
+using ::perfetto::protos::FtraceEvent;
+using ::perfetto::protos::FtraceEventBundle;
+using ::perfetto::protos::InternedData;
+
using ::perfetto::shlib::test_utils::TracingSession;
-using ::perfetto::shlib::test_utils::VarIntField;
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::UnorderedElementsAre;
const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing);
+// TODO(b/303199244): Add tests for all the library functions.
class TracingPerfettoTest : public testing::Test {
protected:
void SetUp() override {
- tracing_perfetto::registerWithPerfetto(true /* test */);
+ tracing_perfetto::registerWithPerfetto(false /* test */);
}
};
-// TODO(b/303199244): Add tests for all the library functions.
-
-TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant,
- REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
- tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, "");
-
+Trace stopSession(TracingSession& tracing_session) {
+ tracing_session.FlushBlocking(5000);
tracing_session.StopBlocking();
std::vector<uint8_t> data = tracing_session.ReadBlocking();
+ std::string data_string(data.begin(), data.end());
+
+ perfetto::protos::Trace trace;
+ trace.ParseFromString(data_string);
+
+ return trace;
+}
+
+void verifyTrackEvent(const Trace& trace, const std::string expected_category,
+ const std::string& expected_name) {
bool found = false;
- for (struct PerfettoPbDecoderField trace_field : FieldView(data)) {
- ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number,
- MsgField(_)));
- IdFieldView track_event(
- trace_field, perfetto_protos_TracePacket_track_event_field_number);
- if (track_event.size() == 0) {
- continue;
+ for (const TracePacket& packet: trace.packet()) {
+ if (packet.has_track_event() && packet.has_interned_data()) {
+
+ const InternedData& interned_data = packet.interned_data();
+ if (interned_data.event_categories_size() > 0) {
+ const EventCategory& event_category = packet.interned_data().event_categories(0);
+ if (event_category.name() == expected_category) {
+ found = true;
+ }
+ }
+
+ if (interned_data.event_names_size() > 0) {
+ const EventName& event_name = packet.interned_data().event_names(0);
+ if (event_name.name() == expected_name) {
+ found &= true;
+ }
+ }
+
+ if (found) {
+ break;
+ }
}
- found = true;
- IdFieldView cat_iid_fields(
- track_event.front(),
- perfetto_protos_TrackEvent_category_iids_field_number);
- ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_)));
- uint64_t cat_iid = cat_iid_fields.front().value.integer64;
- EXPECT_THAT(
- trace_field,
- AllFieldsWithId(
- perfetto_protos_TracePacket_interned_data_field_number,
- ElementsAre(AllFieldsWithId(
- perfetto_protos_InternedData_event_categories_field_number,
- ElementsAre(MsgField(UnorderedElementsAre(
- PbField(perfetto_protos_EventCategory_iid_field_number,
- VarIntField(cat_iid)),
- PbField(perfetto_protos_EventCategory_name_field_number,
- StringField("input")))))))));
}
EXPECT_TRUE(found);
}
-} // namespace tracing_perfetto
\ No newline at end of file
+void verifyAtraceEvent(const Trace& trace, const std::string& expected_name) {
+ std::string expected_print_buf = "I|" + std::to_string(gettid()) + "|" + expected_name + "\n";
+
+ bool found = false;
+ for (const TracePacket& packet: trace.packet()) {
+ if (packet.has_ftrace_events()) {
+ const FtraceEventBundle& ftrace_events_bundle = packet.ftrace_events();
+
+ if (ftrace_events_bundle.event_size() > 0) {
+ const FtraceEvent& ftrace_event = ftrace_events_bundle.event(0);
+ if (ftrace_event.has_print() && (ftrace_event.print().buf() == expected_print_buf)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ EXPECT_TRUE(found);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfetto,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfetto";
+
+ TracingSession tracing_session =
+ TracingSession::Builder().add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyTrackEvent(trace, event_category, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithAtrace,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithAtrace";
+
+ TracingSession tracing_session =
+ TracingSession::Builder().add_atrace_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyAtraceEvent(trace, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtrace,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfettoAndAtrace";
+
+ TracingSession tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyAtraceEvent(trace, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtraceAndPreferTrackEvent,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfettoAndAtraceAndPreferTrackEvent";
+
+ TracingSession tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_atrace_category_prefer_sdk(event_category)
+ .add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyTrackEvent(trace, event_category, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtraceConcurrently,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfettoAndAtraceConcurrently";
+
+ TracingSession perfetto_tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_atrace_category_prefer_sdk(event_category)
+ .add_enabled_category(event_category).Build();
+
+ TracingSession atrace_tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace atrace_trace = stopSession(atrace_tracing_session);
+ Trace perfetto_trace = stopSession(perfetto_tracing_session);
+
+ verifyAtraceEvent(atrace_trace, event_name);
+ verifyAtraceEvent(perfetto_trace, event_name);
+}
+} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp
index 9c42028..8c4d4a8 100644
--- a/libs/tracing_perfetto/tests/utils.cpp
+++ b/libs/tracing_perfetto/tests/utils.cpp
@@ -26,6 +26,11 @@
#include "perfetto/public/protos/config/track_event/track_event_config.pzc.h"
#include "perfetto/public/tracing_session.h"
+#include "protos/perfetto/config/ftrace/ftrace_config.pb.h"
+#include "protos/perfetto/config/track_event/track_event_config.pb.h"
+#include "protos/perfetto/config/data_source_config.pb.h"
+#include "protos/perfetto/config/trace_config.pb.h"
+
namespace perfetto {
namespace shlib {
namespace test_utils {
@@ -44,63 +49,54 @@
} // namespace
TracingSession TracingSession::Builder::Build() {
- struct PerfettoPbMsgWriter writer;
- struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer);
+ perfetto::protos::TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
- struct perfetto_protos_TraceConfig cfg;
- PerfettoPbMsgInit(&cfg.msg, &writer);
+ auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config();
+ auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config();
+
+ track_event_ds_config->set_name("track_event");
+ track_event_ds_config->set_target_buffer(0);
+
+ ftrace_ds_config->set_name("linux.ftrace");
+ ftrace_ds_config->set_target_buffer(0);
{
- struct perfetto_protos_TraceConfig_BufferConfig buffers;
- perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers);
-
- perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024);
-
- perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers);
- }
-
- {
- struct perfetto_protos_TraceConfig_DataSource data_sources;
- perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources);
-
- {
- struct perfetto_protos_DataSourceConfig ds_cfg;
- perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources,
- &ds_cfg);
-
- perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg,
- data_source_name_.c_str());
- if (!enabled_categories_.empty() && !disabled_categories_.empty()) {
- perfetto_protos_TrackEventConfig te_cfg;
- perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg,
- &te_cfg);
- for (const std::string& cat : enabled_categories_) {
- perfetto_protos_TrackEventConfig_set_enabled_categories(
- &te_cfg, cat.data(), cat.size());
- }
- for (const std::string& cat : disabled_categories_) {
- perfetto_protos_TrackEventConfig_set_disabled_categories(
- &te_cfg, cat.data(), cat.size());
- }
- perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg,
- &te_cfg);
+ auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config();
+ if (!atrace_categories_.empty()) {
+ ftrace_config->add_ftrace_events("ftrace/print");
+ for (const std::string& cat : atrace_categories_) {
+ ftrace_config->add_atrace_categories(cat);
}
- perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg);
+ for (const std::string& cat : atrace_categories_prefer_sdk_) {
+ ftrace_config->add_atrace_categories_prefer_sdk(cat);
+ }
}
-
- perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources);
}
- size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer);
- std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]);
- PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size);
- PerfettoHeapBufferDestroy(hb, &writer.writer);
+
+ {
+ auto* track_event_config = track_event_ds_config->mutable_track_event_config();
+ if (!enabled_categories_.empty() || !disabled_categories_.empty()) {
+ for (const std::string& cat : enabled_categories_) {
+ track_event_config->add_enabled_categories(cat);
+ }
+
+ for (const std::string& cat : disabled_categories_) {
+ track_event_config->add_disabled_categories(cat);
+ }
+ }
+ }
struct PerfettoTracingSessionImpl* ts =
- PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS);
+ PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM);
- PerfettoTracingSessionSetup(ts, ser.get(), cfg_size);
+ std::string trace_config_string;
+ trace_config.SerializeToString(&trace_config_string);
+ PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length());
+
+ // Fails to start here
PerfettoTracingSessionStartBlocking(ts);
return TracingSession::Adopt(ts);
diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h
index 4353554..8edb414 100644
--- a/libs/tracing_perfetto/tests/utils.h
+++ b/libs/tracing_perfetto/tests/utils.h
@@ -74,10 +74,6 @@
class Builder {
public:
Builder() = default;
- Builder& set_data_source_name(std::string data_source_name) {
- data_source_name_ = std::move(data_source_name);
- return *this;
- }
Builder& add_enabled_category(std::string category) {
enabled_categories_.push_back(std::move(category));
return *this;
@@ -86,12 +82,21 @@
disabled_categories_.push_back(std::move(category));
return *this;
}
+ Builder& add_atrace_category(std::string category) {
+ atrace_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_atrace_category_prefer_sdk(std::string category) {
+ atrace_categories_prefer_sdk_.push_back(std::move(category));
+ return *this;
+ }
TracingSession Build();
private:
- std::string data_source_name_;
std::vector<std::string> enabled_categories_;
std::vector<std::string> disabled_categories_;
+ std::vector<std::string> atrace_categories_;
+ std::vector<std::string> atrace_categories_prefer_sdk_;
};
static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index 6f716ee..c35e078 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -17,6 +17,7 @@
#include "tracing_perfetto.h"
#include <cutils/trace.h>
+#include <cstdarg>
#include "perfetto/public/te_category_macros.h"
#include "trace_categories.h"
@@ -28,116 +29,172 @@
internal::registerWithPerfetto(test);
}
-Result traceBegin(uint64_t category, const char* name) {
+void traceBegin(uint64_t category, const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceBegin(*perfettoTeCategory, name);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_begin(category, name);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceBegin(*perfettoTeCategory, name);
}
}
-Result traceEnd(uint64_t category) {
+void traceFormatBegin(uint64_t category, const char* fmt, ...) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceEnd(*perfettoTeCategory);
- } else {
+ const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
+ const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+ if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
+ return;
+ }
+
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+
+ if (preferAtrace) {
+ atrace_begin(category, buf);
+ } else if (preferPerfetto) {
+ internal::perfettoTraceBegin(*perfettoTeCategory, buf);
+ }
+}
+
+void traceEnd(uint64_t category) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_end(category);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceEnd(*perfettoTeCategory);
}
}
-Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) {
+void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_begin(category, name, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie);
}
}
-Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) {
+void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_end(category, name, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie);
}
}
-Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+void traceAsyncBeginForTrack(uint64_t category, const char* name,
const char* trackName, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_begin(category, trackName, name, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
}
}
-Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+void traceAsyncEndForTrack(uint64_t category, const char* trackName,
int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_end(category, trackName, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
}
}
-Result traceInstant(uint64_t category, const char* name) {
+void traceInstant(uint64_t category, const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceInstant(*perfettoTeCategory, name);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_instant(category, name);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceInstant(*perfettoTeCategory, name);
}
}
-Result traceInstantForTrack(uint64_t category, const char* trackName,
+void traceFormatInstant(uint64_t category, const char* fmt, ...) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
+ const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+ if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
+ return;
+ }
+
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ if (preferAtrace) {
+ atrace_instant(category, buf);
+ } else if (preferPerfetto) {
+ internal::perfettoTraceInstant(*perfettoTeCategory, buf);
+ }
+}
+
+void traceInstantForTrack(uint64_t category, const char* trackName,
const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_instant_for_track(category, trackName, name);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name);
}
}
-Result traceCounter(uint64_t category, const char* name, int64_t value) {
+void traceCounter(uint64_t category, const char* name, int64_t value) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceCounter(*perfettoTeCategory, name, value);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_int64(category, name, value);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceCounter(*perfettoTeCategory, name, value);
+ }
+}
+
+void traceCounter32(uint64_t category, const char* name, int32_t value) {
+ struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category);
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
+ atrace_int(category, name, value);
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceCounter(*perfettoTeCategory, name,
+ static_cast<int64_t>(value));
}
}
bool isTagEnabled(uint64_t category) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return true;
- } else {
- return (atrace_get_enabled_tags() & category) != 0;
- }
+ return internal::isPerfettoCategoryEnabled(perfettoTeCategory)
+ || atrace_is_tag_enabled(category);
}
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index 758ace6..9a0042a 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -44,13 +44,13 @@
C(rro, "rro", "RRO category") \
C(thermal, "thermal", "Thermal category")
-#include "tracing_perfetto_internal.h"
-
-#include <inttypes.h>
-
+#include <atomic>
#include <mutex>
#include <android_os.h>
+#include <android-base/properties.h>
+#include <cutils/trace.h>
+#include <inttypes.h>
#include "perfetto/public/compiler.h"
#include "perfetto/public/producer.h"
@@ -58,19 +58,42 @@
#include "perfetto/public/te_macros.h"
#include "perfetto/public/track_event.h"
#include "trace_categories.h"
-#include "trace_result.h"
+#include "tracing_perfetto_internal.h"
+
+#ifdef __BIONIC__
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#endif
namespace tracing_perfetto {
namespace internal {
namespace {
-
PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES);
PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES);
-std::atomic_bool is_perfetto_registered = false;
+static constexpr char kPreferFlagProperty[] = "debug.atrace.prefer_sdk";
+static std::atomic<const prop_info*> prefer_property_info = nullptr;
+static std::atomic_uint32_t last_prefer_seq_num = 0;
+static std::atomic_uint64_t prefer_flags = 0;
+
+static const prop_info* system_property_find(const char* name [[maybe_unused]]) {
+ #ifdef __BIONIC__
+ return __system_property_find(name);
+ #endif
+
+ return nullptr;
+}
+
+static uint32_t system_property_serial(const prop_info* pi [[maybe_unused]]) {
+ #ifdef __BIONIC__
+ return __system_property_serial(pi);
+ #endif
+
+ return last_prefer_seq_num;
+}
struct PerfettoTeCategory* toCategory(uint64_t inCategory) {
switch (inCategory) {
@@ -137,8 +160,60 @@
} // namespace
-bool isPerfettoRegistered() {
- return is_perfetto_registered;
+bool isPerfettoCategoryEnabled(PerfettoTeCategory* category) {
+ return category != nullptr;
+}
+
+/**
+ * Updates the cached |prefer_flags|.
+ *
+ * We cache the prefer_flags because reading it on every trace event is expensive.
+ * The cache is invalidated when a sys_prop sequence number changes.
+ */
+void updatePreferFlags() {
+ if (!prefer_property_info.load(std::memory_order_acquire)) {
+ auto* new_prefer_property_info = system_property_find(kPreferFlagProperty);
+ prefer_flags.store(android::base::GetIntProperty(kPreferFlagProperty, 0),
+ std::memory_order_relaxed);
+
+ if (!new_prefer_property_info) {
+ // This should never happen. If it does, we fail gracefully and end up reading the property
+ // traced event.
+ return;
+ }
+
+ last_prefer_seq_num = system_property_serial(new_prefer_property_info);
+ prefer_property_info.store(new_prefer_property_info, std::memory_order_release);
+ }
+
+ uint32_t prefer_seq_num = system_property_serial(prefer_property_info);
+ if (prefer_seq_num != last_prefer_seq_num.load(std::memory_order_acquire)) {
+ prefer_flags.store(android::base::GetIntProperty(kPreferFlagProperty, 0),
+ std::memory_order_relaxed);
+ last_prefer_seq_num.store(prefer_seq_num, std::memory_order_release);
+ }
+}
+
+bool shouldPreferAtrace(PerfettoTeCategory *perfettoCategory, uint64_t atraceCategory) {
+ // There are 3 cases:
+ // 1. Atrace is not enabled.
+ if (!atrace_is_tag_enabled(atraceCategory)) {
+ return false;
+ }
+
+ // 2. Atrace is enabled but perfetto is not enabled.
+ if (!isPerfettoCategoryEnabled(perfettoCategory)) {
+ return true;
+ }
+
+ // Update prefer_flags before checking it below
+ updatePreferFlags();
+
+ // 3. Atrace and perfetto are enabled.
+ // Even though this category is enabled for track events, the config mandates that we downgrade
+ // it to atrace if the same atrace category is currently enabled. This prevents missing the
+ // event from a concurrent session that needs the same category in atrace.
+ return (atraceCategory & prefer_flags.load(std::memory_order_relaxed)) == 0;
}
struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) {
@@ -148,7 +223,7 @@
}
bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
- (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+ (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
return enabled ? perfettoCategory : nullptr;
}
@@ -164,70 +239,57 @@
PerfettoProducerInit(args);
PerfettoTeInit();
PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
- is_perfetto_registered = true;
});
}
-Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) {
+void perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) {
PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name));
- return Result::SUCCESS;
}
-Result perfettoTraceEnd(const struct PerfettoTeCategory& category) {
+void perfettoTraceEnd(const struct PerfettoTeCategory& category) {
PERFETTO_TE(category, PERFETTO_TE_SLICE_END());
- return Result::SUCCESS;
}
-Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
const char* trackName, uint64_t cookie) {
PERFETTO_TE(
category, PERFETTO_TE_SLICE_BEGIN(name),
PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
- return Result::SUCCESS;
}
-Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
const char* trackName, uint64_t cookie) {
PERFETTO_TE(
category, PERFETTO_TE_SLICE_END(),
PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
- return Result::SUCCESS;
}
-Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie) {
- return perfettoTraceAsyncBeginForTrack(category, name, name, cookie);
+ perfettoTraceAsyncBeginForTrack(category, name, name, cookie);
}
-Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie) {
- return perfettoTraceAsyncEndForTrack(category, name, cookie);
+ perfettoTraceAsyncEndForTrack(category, name, cookie);
}
-Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) {
+void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) {
PERFETTO_TE(category, PERFETTO_TE_INSTANT(name));
- return Result::SUCCESS;
}
-Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
const char* trackName, const char* name) {
PERFETTO_TE(
category, PERFETTO_TE_INSTANT(name),
PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid()));
- return Result::SUCCESS;
}
-Result perfettoTraceCounter(const struct PerfettoTeCategory& category,
+void perfettoTraceCounter(const struct PerfettoTeCategory& category,
[[maybe_unused]] const char* name, int64_t value) {
PERFETTO_TE(category, PERFETTO_TE_COUNTER(),
PERFETTO_TE_INT_COUNTER(value));
- return Result::SUCCESS;
}
-
-uint64_t getDefaultCategories() {
- return TRACE_CATEGORIES;
-}
-
} // namespace internal
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
index 79e4b8f..3e1ac2a 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.h
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -19,7 +19,6 @@
#include <stdint.h>
-#include "include/trace_result.h"
#include "perfetto/public/te_category_macros.h"
namespace tracing_perfetto {
@@ -32,31 +31,33 @@
void registerWithPerfetto(bool test = false);
-Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name);
+void perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name);
-Result perfettoTraceEnd(const struct PerfettoTeCategory& category);
+void perfettoTraceEnd(const struct PerfettoTeCategory& category);
-Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie);
-Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie);
-Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
const char* trackName, uint64_t cookie);
-Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
const char* trackName, uint64_t cookie);
-Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name);
+void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name);
-Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
const char* trackName, const char* name);
-Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name,
int64_t value);
-uint64_t getDefaultCategories();
+bool isPerfettoCategoryEnabled(PerfettoTeCategory *perfettoTeCategory);
+
+bool shouldPreferAtrace(PerfettoTeCategory *perfettoTeCategory, uint64_t category);
} // namespace internal
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index e5af740..8b13d78 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -26,6 +26,7 @@
#include <ftl/hash.h>
#include <log/log.h>
#include <ui/DisplayIdentification.h>
+#include <ui/Size.h>
namespace android {
namespace {
@@ -46,6 +47,10 @@
return view[3];
}
+bool isDetailedTimingDescriptor(const byte_view& view) {
+ return view[0] != 0 && view[1] != 0;
+}
+
std::string_view parseEdidText(const byte_view& view) {
std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
text = text.substr(0, text.find('\n'));
@@ -219,6 +224,8 @@
std::string_view displayName;
std::string_view serialNumber;
std::string_view asciiText;
+ ui::Size preferredDTDPixelSize;
+ ui::Size preferredDTDPhysicalSize;
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
@@ -243,6 +250,35 @@
serialNumber = parseEdidText(descriptor);
break;
}
+ } else if (isDetailedTimingDescriptor(view)) {
+ static constexpr size_t kHorizontalPhysicalLsbOffset = 12;
+ static constexpr size_t kHorizontalPhysicalMsbOffset = 14;
+ static constexpr size_t kVerticalPhysicalLsbOffset = 13;
+ static constexpr size_t kVerticalPhysicalMsbOffset = 14;
+ const uint32_t hSize =
+ static_cast<uint32_t>(view[kHorizontalPhysicalLsbOffset] |
+ ((view[kHorizontalPhysicalMsbOffset] >> 4) << 8));
+ const uint32_t vSize =
+ static_cast<uint32_t>(view[kVerticalPhysicalLsbOffset] |
+ ((view[kVerticalPhysicalMsbOffset] & 0b1111) << 8));
+
+ static constexpr size_t kHorizontalPixelLsbOffset = 2;
+ static constexpr size_t kHorizontalPixelMsbOffset = 4;
+ static constexpr size_t kVerticalPixelLsbOffset = 5;
+ static constexpr size_t kVerticalPixelMsbOffset = 7;
+
+ const uint8_t hLsb = view[kHorizontalPixelLsbOffset];
+ const uint8_t hMsb = view[kHorizontalPixelMsbOffset];
+ const int32_t hPixel = hLsb + ((hMsb & 0xF0) << 4);
+
+ const uint8_t vLsb = view[kVerticalPixelLsbOffset];
+ const uint8_t vMsb = view[kVerticalPixelMsbOffset];
+ const int32_t vPixel = vLsb + ((vMsb & 0xF0) << 4);
+
+ preferredDTDPixelSize.setWidth(hPixel);
+ preferredDTDPixelSize.setHeight(vPixel);
+ preferredDTDPhysicalSize.setWidth(hSize);
+ preferredDTDPhysicalSize.setHeight(vSize);
}
view = view.subspan(kDescriptorLength);
@@ -297,14 +333,22 @@
}
}
- return Edid{.manufacturerId = manufacturerId,
- .productId = productId,
- .pnpId = *pnpId,
- .modelHash = modelHash,
- .displayName = displayName,
- .manufactureOrModelYear = manufactureOrModelYear,
- .manufactureWeek = manufactureWeek,
- .cea861Block = cea861Block};
+ DetailedTimingDescriptor preferredDetailedTimingDescriptor{
+ .pixelSizeCount = preferredDTDPixelSize,
+ .physicalSizeInMm = preferredDTDPhysicalSize,
+ };
+
+ return Edid{
+ .manufacturerId = manufacturerId,
+ .productId = productId,
+ .pnpId = *pnpId,
+ .modelHash = modelHash,
+ .displayName = displayName,
+ .manufactureOrModelYear = manufactureOrModelYear,
+ .manufactureWeek = manufactureWeek,
+ .cea861Block = cea861Block,
+ .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor,
+ };
}
std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
@@ -336,9 +380,12 @@
}
const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
- return DisplayIdentificationInfo{.id = displayId,
- .name = std::string(edid->displayName),
- .deviceProductInfo = buildDeviceProductInfo(*edid)};
+ return DisplayIdentificationInfo{
+ .id = displayId,
+ .name = std::string(edid->displayName),
+ .deviceProductInfo = buildDeviceProductInfo(*edid),
+ .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor,
+ };
}
PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 42dd85e..23249fa 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -110,10 +110,12 @@
return mMatrix[i];
}
+// x translate
float Transform::tx() const {
return mMatrix[2][0];
}
+// y translate
float Transform::ty() const {
return mMatrix[2][1];
}
@@ -167,11 +169,15 @@
}
}
-void Transform::set(float a, float b, float c, float d) {
+// x and y are the coordinates in the destination (i.e. the screen)
+// s and t are the coordinates in the source (i.e. the texture)
+// d means derivative
+// dsdx means ds/dx derivative of s with respect to x, etc.
+void Transform::set(float dsdx, float dtdy, float dtdx, float dsdy) {
mat33& M(mMatrix);
- M[0][0] = a; M[1][0] = b;
- M[0][1] = c; M[1][1] = d;
- M[0][2] = 0; M[1][2] = 0;
+ M[0][0] = dsdx; M[1][0] = dtdy;
+ M[0][1] = dtdx; M[1][1] = dsdy;
+ M[0][2] = 0; M[1][2] = 0;
mType = UNKNOWN_TYPE;
}
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index 8bc2017..648e024 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -25,6 +25,7 @@
#include <ui/DeviceProductInfo.h>
#include <ui/DisplayId.h>
+#include <ui/Size.h>
#define LEGACY_DISPLAY_TYPE_PRIMARY 0
#define LEGACY_DISPLAY_TYPE_EXTERNAL 1
@@ -33,10 +34,16 @@
using DisplayIdentificationData = std::vector<uint8_t>;
+struct DetailedTimingDescriptor {
+ ui::Size pixelSizeCount;
+ ui::Size physicalSizeInMm;
+};
+
struct DisplayIdentificationInfo {
PhysicalDisplayId id;
std::string name;
std::optional<DeviceProductInfo> deviceProductInfo;
+ std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
};
struct ExtensionBlock {
@@ -68,6 +75,7 @@
uint8_t manufactureOrModelYear;
uint8_t manufactureWeek;
std::optional<Cea861ExtensionBlock> cea861Block;
+ std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
};
bool isEdid(const DisplayIdentificationData&);
diff --git a/libs/ui/include/ui/EdgeExtensionEffect.h b/libs/ui/include/ui/EdgeExtensionEffect.h
new file mode 100644
index 0000000..02126b1
--- /dev/null
+++ b/libs/ui/include/ui/EdgeExtensionEffect.h
@@ -0,0 +1,95 @@
+/*
+ * 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
+
+/**
+ * Stores the edge that will be extended
+ */
+namespace android {
+
+enum CanonicalDirections { NONE = 0, LEFT = 1, RIGHT = 2, TOP = 4, BOTTOM = 8 };
+
+inline std::string to_string(CanonicalDirections direction) {
+ switch (direction) {
+ case LEFT:
+ return "LEFT";
+ case RIGHT:
+ return "RIGHT";
+ case TOP:
+ return "TOP";
+ case BOTTOM:
+ return "BOTTOM";
+ case NONE:
+ return "NONE";
+ }
+}
+
+struct EdgeExtensionEffect {
+ EdgeExtensionEffect(bool left, bool right, bool top, bool bottom) {
+ extensionEdges = NONE;
+ if (left) {
+ extensionEdges |= LEFT;
+ }
+ if (right) {
+ extensionEdges |= RIGHT;
+ }
+ if (top) {
+ extensionEdges |= TOP;
+ }
+ if (bottom) {
+ extensionEdges |= BOTTOM;
+ }
+ }
+
+ EdgeExtensionEffect() { EdgeExtensionEffect(false, false, false, false); }
+
+ bool extendsEdge(CanonicalDirections edge) const { return extensionEdges & edge; }
+
+ bool hasEffect() const { return extensionEdges != NONE; };
+
+ void reset() { extensionEdges = NONE; }
+
+ bool operator==(const EdgeExtensionEffect& other) const {
+ return extensionEdges == other.extensionEdges;
+ }
+
+ bool operator!=(const EdgeExtensionEffect& other) const { return !(*this == other); }
+
+private:
+ int extensionEdges;
+};
+
+inline std::string to_string(const EdgeExtensionEffect& effect) {
+ std::string s = "EdgeExtensionEffect={edges=[";
+ if (effect.hasEffect()) {
+ for (CanonicalDirections edge : {LEFT, RIGHT, TOP, BOTTOM}) {
+ if (effect.extendsEdge(edge)) {
+ s += to_string(edge) + ", ";
+ }
+ }
+ } else {
+ s += to_string(NONE);
+ }
+ s += "]}";
+ return s;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const EdgeExtensionEffect effect) {
+ os << to_string(effect);
+ return os;
+}
+} // namespace android
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 9aae145..a75ba37 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -156,6 +156,6 @@
base::unique_fd mFenceFd;
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_FENCE_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 652d8ba..936bf8f 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -297,6 +297,6 @@
mDeathCallbacks;
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_GRAPHIC_BUFFER_H
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index bbb2d77..97ed05a 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -137,6 +137,6 @@
};
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_BUFFER_ALLOCATOR_H
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 9da1447..91aabe9 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -188,7 +188,7 @@
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_UI_BUFFER_MAPPER_H
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index cf5c2e8..1f20787 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -72,6 +72,6 @@
uint32_t bytesPerPixel(PixelFormat format);
-}; // namespace android
+} // namespace android
#endif // UI_PIXELFORMAT_H
diff --git a/libs/ui/include/ui/Point.h b/libs/ui/include/ui/Point.h
index d050ede..97a54be 100644
--- a/libs/ui/include/ui/Point.h
+++ b/libs/ui/include/ui/Point.h
@@ -83,6 +83,6 @@
ANDROID_BASIC_TYPES_TRAITS(Point)
-}; // namespace android
+} // namespace android
#endif // ANDROID_UI_POINT
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 9e24a07..2eb9330 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -233,7 +233,7 @@
ANDROID_BASIC_TYPES_TRAITS(Rect)
-}; // namespace android
+} // namespace android
namespace std {
template <>
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 927c334..d1b38f3 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -233,7 +233,7 @@
}
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
namespace std {
template <>
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index 721b466..76e3f66 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -194,6 +194,10 @@
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
EXPECT_FALSE(edid->cea861Block);
+ EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(261, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(163, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getExternalEdid());
ASSERT_TRUE(edid);
@@ -206,6 +210,10 @@
EXPECT_EQ(22, edid->manufactureOrModelYear);
EXPECT_EQ(2, edid->manufactureWeek);
EXPECT_FALSE(edid->cea861Block);
+ EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(641, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(400, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getExternalEedid());
ASSERT_TRUE(edid);
@@ -224,6 +232,10 @@
EXPECT_EQ(0, physicalAddress.b);
EXPECT_EQ(0, physicalAddress.c);
EXPECT_EQ(0, physicalAddress.d);
+ EXPECT_EQ(1366, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(160, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(90, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getPanasonicTvEdid());
ASSERT_TRUE(edid);
@@ -242,6 +254,10 @@
EXPECT_EQ(0, physicalAddress.b);
EXPECT_EQ(0, physicalAddress.c);
EXPECT_EQ(0, physicalAddress.d);
+ EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(698, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(392, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getHisenseTvEdid());
ASSERT_TRUE(edid);
@@ -260,6 +276,10 @@
EXPECT_EQ(2, physicalAddress.b);
EXPECT_EQ(3, physicalAddress.c);
EXPECT_EQ(4, physicalAddress.d);
+ EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(575, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(323, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getCtlDisplayEdid());
ASSERT_TRUE(edid);
@@ -273,6 +293,10 @@
EXPECT_EQ(0xff, edid->manufactureWeek);
ASSERT_TRUE(edid->cea861Block);
EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock);
+ EXPECT_EQ(1360, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(521, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(293, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
}
TEST(DisplayIdentificationTest, parseInvalidEdid) {
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index c97e496..cae2de2 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -93,8 +93,8 @@
externalVibrationScale.scaleLevel);
}
- return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/
- externalVibrationScale.adaptiveHapticsScale};
+ return os::HapticScale(scaleLevel, externalVibrationScale.scaleFactor,
+ externalVibrationScale.adaptiveHapticsScale);
}
} // namespace os
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 706f3d7..ca13afc 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "ExternalVibrationUtils"
+
#include <cstring>
#include <android_os_vibrator.h>
@@ -20,6 +22,7 @@
#include <algorithm>
#include <math.h>
+#include <log/log.h>
#include <vibrator/ExternalVibrationUtils.h>
namespace android::os {
@@ -29,6 +32,7 @@
static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
+static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN
float getOldHapticScaleGamma(HapticLevel level) {
switch (level) {
@@ -60,9 +64,34 @@
}
}
-/* Same as VibrationScaler.SCALE_LEVEL_* */
-float getHapticScaleFactor(HapticLevel level) {
- switch (level) {
+/* Same as VibrationScaler.getScaleFactor */
+float getHapticScaleFactor(HapticScale scale) {
+ if (android_os_vibrator_haptics_scale_v2_enabled()) {
+ if (scale.getScaleFactor() >= 0) {
+ // ExternalVibratorService provided the scale factor, use it.
+ return scale.getScaleFactor();
+ }
+
+ HapticLevel level = scale.getLevel();
+ switch (level) {
+ case HapticLevel::MUTE:
+ return 0.0f;
+ case HapticLevel::NONE:
+ return 1.0f;
+ default:
+ float scaleFactor = powf(SCALE_LEVEL_GAIN, static_cast<int32_t>(level));
+ if (scaleFactor <= 0) {
+ ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0",
+ scaleFactor, static_cast<int32_t>(level));
+ scaleFactor = 1.0f;
+ }
+ return scaleFactor;
+ }
+ }
+ // Same as VibrationScaler.SCALE_FACTOR_*
+ switch (scale.getLevel()) {
+ case HapticLevel::MUTE:
+ return 0.0f;
case HapticLevel::VERY_LOW:
return 0.6f;
case HapticLevel::LOW:
@@ -83,6 +112,14 @@
}
float applyNewHapticScale(float value, float scaleFactor) {
+ if (android_os_vibrator_haptics_scale_v2_enabled()) {
+ if (scaleFactor <= 1 || value == 0) {
+ return value * scaleFactor;
+ } else {
+ // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
+ return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value);
+ }
+ }
float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
if (scaleFactor <= 1) {
// Scale down is simply a gamma corrected application of scaleFactor to the intensity.
@@ -115,21 +152,22 @@
return;
}
HapticLevel hapticLevel = scale.getLevel();
- float scaleFactor = getHapticScaleFactor(hapticLevel);
+ float scaleFactor = getHapticScaleFactor(scale);
float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
float oldGamma = getOldHapticScaleGamma(hapticLevel);
float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
for (size_t i = 0; i < length; i++) {
if (hapticLevel != HapticLevel::NONE) {
- if (android_os_vibrator_fix_audio_coupled_haptics_scaling()) {
+ if (android_os_vibrator_fix_audio_coupled_haptics_scaling() ||
+ android_os_vibrator_haptics_scale_v2_enabled()) {
buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
} else {
buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
}
}
- if (adaptiveScaleFactor != 1.0f) {
+ if (adaptiveScaleFactor >= 0 && adaptiveScaleFactor != 1.0f) {
buffer[i] *= adaptiveScaleFactor;
}
}
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index d9a2b81..f0760fd 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -17,9 +17,13 @@
#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+#include <cstring>
+#include <sstream>
+#include <string>
+
namespace android::os {
-enum class HapticLevel {
+enum class HapticLevel : int32_t {
MUTE = -100,
VERY_LOW = -2,
LOW = -1,
@@ -31,32 +35,44 @@
class HapticScale {
private:
HapticLevel mLevel = HapticLevel::NONE;
+float mScaleFactor = -1.0f; // undefined, use haptic level to define scale factor
float mAdaptiveScaleFactor = 1.0f;
public:
-constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor)
- : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
-constexpr HapticScale(HapticLevel level) : mLevel(level) {}
-constexpr HapticScale() {}
+ explicit HapticScale(HapticLevel level, float scaleFactor, float adaptiveScaleFactor)
+ : mLevel(level), mScaleFactor(scaleFactor), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
+ explicit HapticScale(HapticLevel level) : mLevel(level) {}
+ constexpr HapticScale() {}
-HapticLevel getLevel() const { return mLevel; }
-float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
+ HapticLevel getLevel() const { return mLevel; }
+ float getScaleFactor() const { return mScaleFactor; }
+ float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
-bool operator==(const HapticScale& other) const {
- return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
-}
+ bool operator==(const HapticScale& other) const {
+ return mLevel == other.mLevel && mScaleFactor == other.mScaleFactor &&
+ mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
+ }
bool isScaleNone() const {
- return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f;
+ return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f;
}
bool isScaleMute() const {
- return mLevel == HapticLevel::MUTE;
+ return mLevel == HapticLevel::MUTE || mScaleFactor == 0 || mAdaptiveScaleFactor == 0;
}
-static HapticScale mute() {
- return {/*level=*/os::HapticLevel::MUTE};
+std::string toString() const {
+ std::ostringstream os;
+ os << "HapticScale { level: " << static_cast<int>(mLevel);
+ os << ", scaleFactor: " << mScaleFactor;
+ os << ", adaptiveScaleFactor: " << mAdaptiveScaleFactor;
+ os << "}";
+ return os.str();
}
+
+static HapticScale mute() { return os::HapticScale(os::HapticLevel::MUTE); }
+
+static HapticScale none() { return os::HapticScale(os::HapticLevel::NONE); }
};
bool isValidHapticScale(HapticScale scale);
diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp
index 3141380..4133836 100644
--- a/libs/vibrator/tests/ExternalVibrationTest.cpp
+++ b/libs/vibrator/tests/ExternalVibrationTest.cpp
@@ -57,11 +57,14 @@
originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE;
+
sp<TestVibrationController> vibrationController = new TestVibrationController();
ASSERT_NE(vibrationController, nullptr);
+
sp<os::ExternalVibration> original =
new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController);
ASSERT_NE(original, nullptr);
+
EXPECT_EQ(original->getUid(), uid);
EXPECT_EQ(original->getPackage(), pkg);
EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type);
@@ -69,18 +72,22 @@
EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source);
EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags);
EXPECT_EQ(original->getController(), vibrationController);
+
audio_attributes_t defaultAttrs;
defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
defaultAttrs.usage = AUDIO_USAGE_UNKNOWN;
defaultAttrs.source = AUDIO_SOURCE_DEFAULT;
defaultAttrs.flags = AUDIO_FLAG_NONE;
+
sp<os::ExternalVibration> parceled =
new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr);
ASSERT_NE(parceled, nullptr);
+
Parcel parcel;
original->writeToParcel(&parcel);
parcel.setDataPosition(0);
parceled->readFromParcel(&parcel);
+
EXPECT_EQ(parceled->getUid(), uid);
EXPECT_EQ(parceled->getPackage(), pkg);
EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type);
@@ -93,12 +100,17 @@
TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) {
os::ExternalVibrationScale externalVibrationScale;
externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH;
+ externalVibrationScale.scaleFactor = 0.5f;
externalVibrationScale.adaptiveHapticsScale = 0.8f;
+
os::HapticScale hapticScale =
os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
- // Check scale factor is forwarded.
+
+ // Check scale factors are forwarded.
EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH);
+ EXPECT_EQ(hapticScale.getScaleFactor(), 0.5f);
EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f);
+
// Check conversion for all levels.
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE);
EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW);
diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
index 3d8dd9c..9369f80 100644
--- a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
+++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
@@ -41,7 +41,7 @@
protected:
void scaleBuffer(HapticLevel hapticLevel) {
- scaleBuffer(HapticScale(hapticLevel), 0 /* limit */);
+ scaleBuffer(HapticScale(hapticLevel));
}
void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) {
@@ -49,7 +49,11 @@
}
void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) {
- scaleBuffer(HapticScale(hapticLevel, adaptiveScaleFactor), limit);
+ scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit);
+ }
+
+ void scaleBuffer(HapticScale hapticScale) {
+ scaleBuffer(hapticScale, 0 /* limit */);
}
void scaleBuffer(HapticScale hapticScale, float limit) {
@@ -60,11 +64,19 @@
float mBuffer[TEST_BUFFER_LENGTH];
};
-TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestLegacyScaleMute,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleMute,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleMute,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
float expected[TEST_BUFFER_LENGTH];
std::fill(std::begin(expected), std::end(expected), 0);
@@ -73,10 +85,9 @@
}
TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestFixedScaleMute,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+ ExternalVibrationUtilsTest, TestScaleV2Mute,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
float expected[TEST_BUFFER_LENGTH];
std::fill(std::begin(expected), std::end(expected), 0);
@@ -84,11 +95,19 @@
EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
-TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestLegacyScaleNone,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleNone,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleNone,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
float expected[TEST_BUFFER_LENGTH];
std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
@@ -97,10 +116,9 @@
}
TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestFixedScaleNone,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+ ExternalVibrationUtilsTest, TestScaleV2None,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
float expected[TEST_BUFFER_LENGTH];
std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
@@ -108,11 +126,9 @@
EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
-TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestLegacyScaleToHapticLevel,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleToHapticLevel,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.84f, -0.66f };
scaleBuffer(HapticLevel::VERY_HIGH);
EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
@@ -130,11 +146,9 @@
EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
-TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestFixedScaleToHapticLevel,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleToHapticLevel,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.79f, -0.39f };
scaleBuffer(HapticLevel::VERY_HIGH);
EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
@@ -153,10 +167,76 @@
}
TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestAdaptiveScaleFactorAppliedAfterLegacyScale,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+ ExternalVibrationUtilsTest, TestScaleV2ToHapticLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.8f, -0.38f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.63f, -0.27f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.71f, -0.71f, 0.35f, -0.14f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.51f, -0.51f, 0.25f, -0.1f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorUndefinedUsesHapticLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ constexpr float adaptiveScaleNone = 1.0f;
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.8f, -0.38f};
+ scaleBuffer(HapticScale(HapticLevel::VERY_HIGH, -1.0f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorIgnoresLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ constexpr float adaptiveScaleNone = 1.0f;
+
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 1, -0.55f };
+ scaleBuffer(HapticScale(HapticLevel::LOW, 3.0f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.66f, -0.29f };
+ scaleBuffer(HapticScale(HapticLevel::LOW, 1.5f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.8f, -0.8f, 0.4f, -0.16f };
+ scaleBuffer(HapticScale(HapticLevel::HIGH, 0.8f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.4f, -0.4f, 0.2f, -0.08f };
+ scaleBuffer(HapticScale(HapticLevel::HIGH, 0.4f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIsIgnoredLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.79f, -0.39f};
+ scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Adaptive scale mutes vibration
+ float expectedMuted[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0);
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
// Haptic level scale up then adaptive scale down
float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.13f };
scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
@@ -178,11 +258,23 @@
EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
-TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestAdaptiveScaleFactorAppliedAfterFixedScale,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIgnoredFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.79f, -0.39f};
+ scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Adaptive scale mutes vibration
+ float expectedMuted[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0);
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
// Haptic level scale up then adaptive scale down
float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.07f };
scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
@@ -205,10 +297,48 @@
}
TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestLimitAppliedAfterLegacyScale,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+ ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIgnoredScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.8f, -0.38f};
+ scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Adaptive scale mutes vibration
+ float expectedMuted[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0);
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.95f, -0.41f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.02f, -1.02f, 0.51f, -0.2f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
// Scaled = { 0.2, -0.2, 0.16f, -0.13f };
float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.13f };
scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
@@ -220,11 +350,9 @@
EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
-TEST_F_WITH_FLAGS(
- ExternalVibrationUtilsTest,
- TestLimitAppliedAfterFixedScale,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling))
-) {
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
// Scaled = { 0.2, -0.2, 0.16f, -0.13f };
float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
@@ -235,3 +363,18 @@
scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestLimitAppliedAfterScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.15f, -0.07f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1.02f, -1.02f, 0.51f, -0.2f }
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.51f, -0.2f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index c9e8b7c..782b6d9 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -6,39 +6,24 @@
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
-** This header is generated from the Khronos OpenGL / OpenGL ES XML
-** API Registry. The current version of the Registry, generator scripts
+** This header is generated from the Khronos EGL XML API Registry.
+** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
+** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $
*/
#include <EGL/eglplatform.h>
-/* Generated on date 20180517 */
+#ifndef EGL_EGL_PROTOTYPES
+#define EGL_EGL_PROTOTYPES 1
+#endif
+
+/* Generated on date 20240715 */
/* Generated C header for:
* API: egl
@@ -118,6 +103,31 @@
#define EGL_VERSION 0x3054
#define EGL_WIDTH 0x3057
#define EGL_WINDOW_BIT 0x0004
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCHOOSECONFIGPROC) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCOPYBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
+typedef EGLContext (EGLAPIENTRYP PFNEGLCREATECONTEXTPROC) (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERSURFACEPROC) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGATTRIBPROC) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGSPROC) (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETCURRENTDISPLAYPROC) (void);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLGETCURRENTSURFACEPROC) (EGLint readdraw);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETDISPLAYPROC) (EGLNativeDisplayType display_id);
+typedef EGLint (EGLAPIENTRYP PFNEGLGETERRORPROC) (void);
+typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC) (const char *procname);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLINITIALIZEPROC) (EGLDisplay dpy, EGLint *major, EGLint *minor);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLMAKECURRENTPROC) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLTERMINATEPROC) (EGLDisplay dpy);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITGLPROC) (void);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITNATIVEPROC) (EGLint engine);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
EGLAPI EGLContext EGLAPIENTRY eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
@@ -142,6 +152,7 @@
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate (EGLDisplay dpy);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL (void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative (EGLint engine);
+#endif
#endif /* EGL_VERSION_1_0 */
#ifndef EGL_VERSION_1_1
@@ -160,10 +171,16 @@
#define EGL_TEXTURE_RGB 0x305D
#define EGL_TEXTURE_RGBA 0x305E
#define EGL_TEXTURE_TARGET 0x3081
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDTEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSURFACEATTRIBPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPINTERVALPROC) (EGLDisplay dpy, EGLint interval);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval (EGLDisplay dpy, EGLint interval);
+#endif
#endif /* EGL_VERSION_1_1 */
#ifndef EGL_VERSION_1_2
@@ -199,11 +216,18 @@
#define EGL_SWAP_BEHAVIOR 0x3093
#define EGL_UNKNOWN EGL_CAST(EGLint,-1)
#define EGL_VERTICAL_RESOLUTION 0x3091
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDAPIPROC) (EGLenum api);
+typedef EGLenum (EGLAPIENTRYP PFNEGLQUERYAPIPROC) (void);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC) (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETHREADPROC) (void);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITCLIENTPROC) (void);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI (EGLenum api);
EGLAPI EGLenum EGLAPIENTRY eglQueryAPI (void);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread (void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void);
+#endif
#endif /* EGL_VERSION_1_2 */
#ifndef EGL_VERSION_1_3
@@ -232,7 +256,10 @@
#define EGL_OPENGL_API 0x30A2
#define EGL_OPENGL_BIT 0x0008
#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400
+typedef EGLContext (EGLAPIENTRYP PFNEGLGETCURRENTCONTEXTPROC) (void);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
+#endif
#endif /* EGL_VERSION_1_4 */
#ifndef EGL_VERSION_1_5
@@ -284,6 +311,17 @@
#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
#define EGL_IMAGE_PRESERVED 0x30D2
#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)
+typedef EGLSync (EGLAPIENTRYP PFNEGLCREATESYNCPROC) (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCPROC) (EGLDisplay dpy, EGLSync sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBPROC) (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
@@ -294,6 +332,7 @@
EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif
#endif /* EGL_VERSION_1_5 */
#ifdef __cplusplus
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index c787fc9..4d14c69 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -6,39 +6,20 @@
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
** This header is generated from the Khronos EGL XML API Registry.
** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: 726475c203 $ on $Git commit date: 2018-10-03 23:51:49 -0700 $
+** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $
*/
#include <EGL/eglplatform.h>
-#define EGL_EGLEXT_VERSION 20181204
+#define EGL_EGLEXT_VERSION 20240715
/* Generated C header for:
* API: egl
@@ -443,9 +424,9 @@
#ifndef EGL_KHR_swap_buffers_with_damage
#define EGL_KHR_swap_buffers_with_damage 1
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#endif
#endif /* EGL_KHR_swap_buffers_with_damage */
@@ -462,6 +443,10 @@
#endif
#endif /* EGL_KHR_wait_sync */
+#ifndef EGL_ANDROID_GLES_layers
+#define EGL_ANDROID_GLES_layers 1
+#endif /* EGL_ANDROID_GLES_layers */
+
#ifndef EGL_ANDROID_blob_cache
#define EGL_ANDROID_blob_cache 1
typedef khronos_ssize_t EGLsizeiANDROID;
@@ -566,6 +551,11 @@
#define EGL_RECORDABLE_ANDROID 0x3142
#endif /* EGL_ANDROID_recordable */
+#ifndef EGL_ANDROID_telemetry_hint
+#define EGL_ANDROID_telemetry_hint 1
+#define EGL_TELEMETRY_HINT_ANDROID 0x3570
+#endif /* EGL_ANDROID_telemetry_hint */
+
#ifndef EGL_ANGLE_d3d_share_handle_client_buffer
#define EGL_ANGLE_d3d_share_handle_client_buffer 1
#define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
@@ -589,11 +579,25 @@
#define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1
#endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */
+#ifndef EGL_ANGLE_sync_control_rate
+#define EGL_ANGLE_sync_control_rate 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETMSCRATEANGLEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateANGLE (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator);
+#endif
+#endif /* EGL_ANGLE_sync_control_rate */
+
#ifndef EGL_ANGLE_window_fixed_size
#define EGL_ANGLE_window_fixed_size 1
#define EGL_FIXED_SIZE_ANGLE 0x3201
#endif /* EGL_ANGLE_window_fixed_size */
+#ifndef EGL_ARM_image_format
+#define EGL_ARM_image_format 1
+#define EGL_COLOR_COMPONENT_TYPE_UNSIGNED_INTEGER_ARM 0x3287
+#define EGL_COLOR_COMPONENT_TYPE_INTEGER_ARM 0x3288
+#endif /* EGL_ARM_image_format */
+
#ifndef EGL_ARM_implicit_external_sync
#define EGL_ARM_implicit_external_sync 1
#define EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM 0x328A
@@ -652,6 +656,11 @@
#endif
#endif /* EGL_EXT_compositor */
+#ifndef EGL_EXT_config_select_group
+#define EGL_EXT_config_select_group 1
+#define EGL_CONFIG_SELECT_GROUP_EXT 0x34C0
+#endif /* EGL_EXT_config_select_group */
+
#ifndef EGL_EXT_create_context_robustness
#define EGL_EXT_create_context_robustness 1
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
@@ -684,6 +693,11 @@
#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif /* EGL_EXT_device_drm */
+#ifndef EGL_EXT_device_drm_render_node
+#define EGL_EXT_device_drm_render_node 1
+#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
+#endif /* EGL_EXT_device_drm_render_node */
+
#ifndef EGL_EXT_device_enumeration
#define EGL_EXT_device_enumeration 1
#endif /* EGL_EXT_device_enumeration */
@@ -691,12 +705,33 @@
#ifndef EGL_EXT_device_openwf
#define EGL_EXT_device_openwf 1
#define EGL_OPENWF_DEVICE_ID_EXT 0x3237
+#define EGL_OPENWF_DEVICE_EXT 0x333D
#endif /* EGL_EXT_device_openwf */
+#ifndef EGL_EXT_device_persistent_id
+#define EGL_EXT_device_persistent_id 1
+#define EGL_DEVICE_UUID_EXT 0x335C
+#define EGL_DRIVER_UUID_EXT 0x335D
+#define EGL_DRIVER_NAME_EXT 0x335E
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICEBINARYEXTPROC) (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDeviceBinaryEXT (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size);
+#endif
+#endif /* EGL_EXT_device_persistent_id */
+
#ifndef EGL_EXT_device_query
#define EGL_EXT_device_query 1
#endif /* EGL_EXT_device_query */
+#ifndef EGL_EXT_device_query_name
+#define EGL_EXT_device_query_name 1
+#define EGL_RENDERER_EXT 0x335F
+#endif /* EGL_EXT_device_query_name */
+
+#ifndef EGL_EXT_explicit_device
+#define EGL_EXT_explicit_device 1
+#endif /* EGL_EXT_explicit_device */
+
#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
#define EGL_EXT_gl_colorspace_bt2020_hlg 1
#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x3540
@@ -878,6 +913,17 @@
#define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6
#endif /* EGL_EXT_platform_x11 */
+#ifndef EGL_EXT_platform_xcb
+#define EGL_EXT_platform_xcb 1
+#define EGL_PLATFORM_XCB_EXT 0x31DC
+#define EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE
+#endif /* EGL_EXT_platform_xcb */
+
+#ifndef EGL_EXT_present_opaque
+#define EGL_EXT_present_opaque 1
+#define EGL_PRESENT_OPAQUE_EXT 0x31DF
+#endif /* EGL_EXT_present_opaque */
+
#ifndef EGL_EXT_protected_content
#define EGL_EXT_protected_content 1
#define EGL_PROTECTED_CONTENT_EXT 0x32C0
@@ -887,6 +933,10 @@
#define EGL_EXT_protected_surface 1
#endif /* EGL_EXT_protected_surface */
+#ifndef EGL_EXT_query_reset_notification_strategy
+#define EGL_EXT_query_reset_notification_strategy 1
+#endif /* EGL_EXT_query_reset_notification_strategy */
+
#ifndef EGL_EXT_stream_consumer_egloutput
#define EGL_EXT_stream_consumer_egloutput 1
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer);
@@ -916,11 +966,36 @@
#define EGL_METADATA_SCALING_EXT 50000
#endif /* EGL_EXT_surface_SMPTE2086_metadata */
+#ifndef EGL_EXT_surface_compression
+#define EGL_EXT_surface_compression 1
+#define EGL_SURFACE_COMPRESSION_EXT 0x34B0
+#define EGL_SURFACE_COMPRESSION_PLANE1_EXT 0x328E
+#define EGL_SURFACE_COMPRESSION_PLANE2_EXT 0x328F
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT 0x34B1
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT 0x34B2
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT 0x34B4
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_2BPC_EXT 0x34B5
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_3BPC_EXT 0x34B6
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_4BPC_EXT 0x34B7
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_5BPC_EXT 0x34B8
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_6BPC_EXT 0x34B9
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_7BPC_EXT 0x34BA
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_8BPC_EXT 0x34BB
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_9BPC_EXT 0x34BC
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_10BPC_EXT 0x34BD
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_11BPC_EXT 0x34BE
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT 0x34BF
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSUPPORTEDCOMPRESSIONRATESEXTPROC) (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQuerySupportedCompressionRatesEXT (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates);
+#endif
+#endif /* EGL_EXT_surface_compression */
+
#ifndef EGL_EXT_swap_buffers_with_damage
#define EGL_EXT_swap_buffers_with_damage 1
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#endif
#endif /* EGL_EXT_swap_buffers_with_damage */
@@ -1036,6 +1111,16 @@
#define EGL_PLATFORM_SURFACELESS_MESA 0x31DD
#endif /* EGL_MESA_platform_surfaceless */
+#ifndef EGL_MESA_query_driver
+#define EGL_MESA_query_driver 1
+typedef char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERCONFIGPROC) (EGLDisplay dpy);
+typedef const char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERNAMEPROC) (EGLDisplay dpy);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI char *EGLAPIENTRY eglGetDisplayDriverConfig (EGLDisplay dpy);
+EGLAPI const char *EGLAPIENTRY eglGetDisplayDriverName (EGLDisplay dpy);
+#endif
+#endif /* EGL_MESA_query_driver */
+
#ifndef EGL_NOK_swap_region
#define EGL_NOK_swap_region 1
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGIONNOKPROC) (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
@@ -1124,11 +1209,39 @@
#endif
#endif /* EGL_NV_post_sub_buffer */
+#ifndef EGL_NV_quadruple_buffer
+#define EGL_NV_quadruple_buffer 1
+#define EGL_QUADRUPLE_BUFFER_NV 0x3231
+#endif /* EGL_NV_quadruple_buffer */
+
#ifndef EGL_NV_robustness_video_memory_purge
#define EGL_NV_robustness_video_memory_purge 1
#define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C
#endif /* EGL_NV_robustness_video_memory_purge */
+#ifndef EGL_NV_stream_consumer_eglimage
+#define EGL_NV_stream_consumer_eglimage 1
+#define EGL_STREAM_CONSUMER_IMAGE_NV 0x3373
+#define EGL_STREAM_IMAGE_ADD_NV 0x3374
+#define EGL_STREAM_IMAGE_REMOVE_NV 0x3375
+#define EGL_STREAM_IMAGE_AVAILABLE_NV 0x3376
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMIMAGECONSUMERCONNECTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list);
+typedef EGLint (EGLAPIENTRYP PFNEGLQUERYSTREAMCONSUMEREVENTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMACQUIREIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMRELEASEIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamImageConsumerConnectNV (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list);
+EGLAPI EGLint EGLAPIENTRY eglQueryStreamConsumerEventNV (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamAcquireImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamReleaseImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync);
+#endif
+#endif /* EGL_NV_stream_consumer_eglimage */
+
+#ifndef EGL_NV_stream_consumer_eglimage_use_scanout_attrib
+#define EGL_NV_stream_consumer_eglimage_use_scanout_attrib 1
+#define EGL_STREAM_CONSUMER_IMAGE_USE_SCANOUT_NV 0x3378
+#endif /* EGL_NV_stream_consumer_eglimage_use_scanout_attrib */
+
#ifndef EGL_NV_stream_consumer_gltexture_yuv
#define EGL_NV_stream_consumer_gltexture_yuv 1
#define EGL_YUV_PLANE0_TEXTURE_UNIT_NV 0x332C
@@ -1165,6 +1278,12 @@
#define EGL_STREAM_CROSS_SYSTEM_NV 0x334F
#endif /* EGL_NV_stream_cross_system */
+#ifndef EGL_NV_stream_dma
+#define EGL_NV_stream_dma 1
+#define EGL_STREAM_DMA_NV 0x3371
+#define EGL_STREAM_DMA_SERVER_NV 0x3372
+#endif /* EGL_NV_stream_dma */
+
#ifndef EGL_NV_stream_fifo_next
#define EGL_NV_stream_fifo_next 1
#define EGL_PENDING_FRAME_NV 0x3329
@@ -1216,6 +1335,21 @@
#endif
#endif /* EGL_NV_stream_metadata */
+#ifndef EGL_NV_stream_origin
+#define EGL_NV_stream_origin 1
+#define EGL_STREAM_FRAME_ORIGIN_X_NV 0x3366
+#define EGL_STREAM_FRAME_ORIGIN_Y_NV 0x3367
+#define EGL_STREAM_FRAME_MAJOR_AXIS_NV 0x3368
+#define EGL_CONSUMER_AUTO_ORIENTATION_NV 0x3369
+#define EGL_PRODUCER_AUTO_ORIENTATION_NV 0x336A
+#define EGL_LEFT_NV 0x336B
+#define EGL_RIGHT_NV 0x336C
+#define EGL_TOP_NV 0x336D
+#define EGL_BOTTOM_NV 0x336E
+#define EGL_X_AXIS_NV 0x336F
+#define EGL_Y_AXIS_NV 0x3370
+#endif /* EGL_NV_stream_origin */
+
#ifndef EGL_NV_stream_remote
#define EGL_NV_stream_remote 1
#define EGL_STREAM_STATE_INITIALIZING_NV 0x3240
@@ -1312,6 +1446,21 @@
#endif /* KHRONOS_SUPPORT_INT64 */
#endif /* EGL_NV_system_time */
+#ifndef EGL_NV_triple_buffer
+#define EGL_NV_triple_buffer 1
+#define EGL_TRIPLE_BUFFER_NV 0x3230
+#endif /* EGL_NV_triple_buffer */
+
+#ifndef EGL_QNX_image_native_buffer
+#define EGL_QNX_image_native_buffer 1
+#define EGL_NATIVE_BUFFER_QNX 0x3551
+#endif /* EGL_QNX_image_native_buffer */
+
+#ifndef EGL_QNX_platform_screen
+#define EGL_QNX_platform_screen 1
+#define EGL_PLATFORM_SCREEN_QNX 0x3550
+#endif /* EGL_QNX_platform_screen */
+
#ifndef EGL_TIZEN_image_native_buffer
#define EGL_TIZEN_image_native_buffer 1
#define EGL_NATIVE_BUFFER_TIZEN 0x32A0
@@ -1322,6 +1471,40 @@
#define EGL_NATIVE_SURFACE_TIZEN 0x32A1
#endif /* EGL_TIZEN_image_native_surface */
+#ifndef EGL_WL_bind_wayland_display
+#define EGL_WL_bind_wayland_display 1
+#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC
+struct wl_display;
+struct wl_resource;
+#define EGL_WAYLAND_BUFFER_WL 0x31D5
+#define EGL_WAYLAND_PLANE_WL 0x31D6
+#define EGL_TEXTURE_Y_U_V_WL 0x31D7
+#define EGL_TEXTURE_Y_UV_WL 0x31D8
+#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWLPROC) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#endif
+#endif /* EGL_WL_bind_wayland_display */
+
+#ifndef EGL_WL_create_wayland_buffer_from_image
+#define EGL_WL_create_wayland_buffer_from_image 1
+#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC
+struct wl_buffer;
+typedef struct wl_buffer *(EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC) (EGLDisplay dpy, EGLImageKHR image);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI struct wl_buffer *EGLAPIENTRY eglCreateWaylandBufferFromImageWL (EGLDisplay dpy, EGLImageKHR image);
+#endif
+#endif /* EGL_WL_create_wayland_buffer_from_image */
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 0bc2cb9..6786afd 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -2,36 +2,17 @@
#define __eglplatform_h_
/*
-** Copyright (c) 2007-2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+** Copyright 2007-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
*/
/* Platform-specific types and definitions for egl.h
- * $Revision: 30994 $ on $Date: 2015-04-30 13:36:48 -0700 (Thu, 30 Apr 2015) $
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
* they can be included in future versions of this file. Please submit changes
- * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
- * by filing a bug against product "EGL" component "Registry".
+ * by filing an issue or pull request on the public Khronos EGL Registry, at
+ * https://www.github.com/KhronosGroup/EGL-Registry/
*/
#include <KHR/khrplatform.h>
@@ -67,7 +48,13 @@
* implementations.
*/
-#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES)
+
+typedef void *EGLNativeDisplayType;
+typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
+
+#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
@@ -77,11 +64,23 @@
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
+#elif defined(__QNX__)
+
+typedef khronos_uintptr_t EGLNativeDisplayType;
+typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */
+typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */
+
+#elif defined(__EMSCRIPTEN__)
+
+typedef int EGLNativeDisplayType;
+typedef int EGLNativePixmapType;
+typedef int EGLNativeWindowType;
+
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
-typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
#elif defined(WL_EGL_PLATFORM)
@@ -100,17 +99,17 @@
struct ANativeWindow;
struct egl_native_pixmap_t;
-typedef struct ANativeWindow* EGLNativeWindowType;
-typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
+typedef struct egl_native_pixmap_t* EGLNativePixmapType;
+typedef struct ANativeWindow* EGLNativeWindowType;
#elif defined(USE_OZONE)
typedef intptr_t EGLNativeDisplayType;
-typedef intptr_t EGLNativeWindowType;
typedef intptr_t EGLNativePixmapType;
+typedef intptr_t EGLNativeWindowType;
-#elif defined(__unix__) || defined(USE_X11)
+#elif defined(USE_X11)
/* X11 (tentative) */
#include <X11/Xlib.h>
@@ -120,11 +119,17 @@
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
+#elif defined(__unix__)
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#elif defined(__APPLE__)
typedef int EGLNativeDisplayType;
-typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
#elif defined(__HAIKU__)
@@ -134,6 +139,12 @@
typedef khronos_uintptr_t EGLNativePixmapType;
typedef khronos_uintptr_t EGLNativeWindowType;
+#elif defined(__Fuchsia__)
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#else
#error "Platform not recognized"
#endif
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 502c14f..8cb637b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -248,7 +248,7 @@
return cnx->platform.eglGetProcAddress(procname);
}
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, const EGLint* rects,
EGLint n_rects) {
ATRACE_CALL();
clearError();
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 1c91f1d..b6f2c34 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -106,5 +106,5 @@
/* Partial update extensions */
-EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint *, EGLint)
EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 0bfefd6..6713a5c 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -916,42 +916,72 @@
egl_context_t* const c = get_context(share_list);
share_list = c->context;
}
+
+ bool skip_telemetry = false;
+
+ auto findAttribute = [](const EGLint* attrib_ptr, GLint attribute, GLint* value) {
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr == attribute) {
+ if (value) {
+ *value = val;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ std::vector<EGLint> replacement_attrib_list;
+ GLint telemetry_value;
+ if (findAttribute(attrib_list, EGL_TELEMETRY_HINT_ANDROID, &telemetry_value)) {
+ skip_telemetry = (telemetry_value == android::GpuStatsInfo::SKIP_TELEMETRY);
+
+ // We need to remove EGL_TELEMETRY_HINT_ANDROID or the underlying drivers will
+ // complain about an unexpected attribute
+ const EGLint* attrib_ptr = attrib_list;
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr != EGL_TELEMETRY_HINT_ANDROID) {
+ replacement_attrib_list.push_back(attr);
+ replacement_attrib_list.push_back(val);
+ }
+ }
+ replacement_attrib_list.push_back(EGL_NONE);
+ attrib_list = replacement_attrib_list.data();
+ }
// b/111083885 - If we are presenting EGL 1.4 interface to apps
// error out on robust access attributes that are invalid
// in EGL 1.4 as the driver may be fine with them but dEQP expects
// tests to fail according to spec.
if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
- const EGLint* attrib_ptr = attrib_list;
- while (*attrib_ptr != EGL_NONE) {
- GLint attr = *attrib_ptr++;
- GLint value = *attrib_ptr++;
- if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
- // We are GL ES context with EGL 1.4, this is an invalid
- // attribute
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
- }
- };
+ if (findAttribute(attrib_list, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
+ nullptr)) {
+ // We are GL ES context with EGL 1.4, this is an invalid attribute
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
}
EGLContext context =
cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- };
+ GLint version_value;
+ if (findAttribute(attrib_list, EGL_CONTEXT_CLIENT_VERSION, &version_value)) {
+ if (version_value == 2 || version_value == 3) {
+ version = egl_connection_t::GLESv2_INDEX;
+ }
}
if (version == egl_connection_t::GLESv1_INDEX) {
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::GLES_1_IN_USE);
}
- android::GraphicsEnv::getInstance().setTargetStats(
- android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ if (!skip_telemetry) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ }
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
return c;
}
@@ -1324,7 +1354,7 @@
std::mutex mMutex;
};
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, const EGLint* rects,
EGLint n_rects) {
const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
index 4a0d4b9..dc99e09 100644
--- a/opengl/libs/GLES2/gl2ext_api.in
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -427,9 +427,6 @@
void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex);
}
-void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
- CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex);
-}
void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) {
CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount);
}
diff --git a/opengl/libs/entries.in b/opengl/libs/entries.in
index a306510..4c1eefc 100644
--- a/opengl/libs/entries.in
+++ b/opengl/libs/entries.in
@@ -605,7 +605,6 @@
GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride)
GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
-GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount)
GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride)
GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
index 4673411..004aa5a 100644
--- a/opengl/libs/platform_entries.in
+++ b/opengl/libs/platform_entries.in
@@ -24,7 +24,7 @@
EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
EGL_ENTRY(EGLint, eglGetError, void)
EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
-EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint*, EGLint)
EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 3ef5049..da1aae2 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -152,6 +152,12 @@
data.writeNullableParcelable(extras);
return remote()->transact(PORT_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual status_t permissionUpdateBarrier() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ return remote()->transact(PERMISSION_UPDATE_BARRIER, data, &reply, 0);
+ }
};
IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService");
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index 1a744ab..7628745 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -44,7 +44,7 @@
#include "gpuwork/gpuWork.h"
-#define ONE_MS_IN_NS (10000000)
+#define MSEC_PER_NSEC (1000LU * 1000LU)
namespace android {
namespace gpuwork {
@@ -118,6 +118,9 @@
}
void GpuWork::initialize() {
+ // Workaround b/347947040 by allowing time for statsd / bpf setup.
+ std::this_thread::sleep_for(std::chrono::seconds(30));
+
// Make sure BPF programs are loaded.
bpf::waitForProgsLoaded();
@@ -382,10 +385,11 @@
ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size());
auto now = std::chrono::steady_clock::now();
- long long duration =
- std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
- .count();
- if (duration > std::numeric_limits<int32_t>::max() || duration < 0) {
+ int32_t duration =
+ static_cast<int32_t>(
+ std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
+ .count());
+ if (duration < 0) {
// This is essentially impossible. If it does somehow happen, give up,
// but still clear the map.
clearMap();
@@ -401,13 +405,14 @@
}
const UidTrackingInfo& info = it->second;
- uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS;
- uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS;
+ int32_t total_active_duration_ms =
+ static_cast<int32_t>(info.total_active_duration_ns / MSEC_PER_NSEC);
+ int32_t total_inactive_duration_ms =
+ static_cast<int32_t>(info.total_inactive_duration_ns / MSEC_PER_NSEC);
// Skip this atom if any numbers are out of range. |duration| is
// already checked above.
- if (total_active_duration_ms > std::numeric_limits<int32_t>::max() ||
- total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) {
+ if (total_active_duration_ms < 0 || total_inactive_duration_ms < 0) {
continue;
}
@@ -418,11 +423,11 @@
// gpu_id
bitcast_int32(gpuId),
// time_duration_seconds
- static_cast<int32_t>(duration),
+ duration,
// total_active_duration_millis
- static_cast<int32_t>(total_active_duration_ms),
+ total_active_duration_ms,
// total_inactive_duration_millis
- static_cast<int32_t>(total_inactive_duration_ms));
+ total_inactive_duration_ms);
}
}
clearMap();
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
index e70da54..60cd2c7 100644
--- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -125,7 +125,7 @@
static constexpr size_t kNumGpusHardLimit = 32;
// The minimum GPU time needed to actually log stats for a UID.
- static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds.
+ static constexpr uint64_t kMinGpuTimeNanoseconds = 10LLU * 1000000000LLU; // 10 seconds.
// The previous time point at which |mGpuWorkMap| was cleared.
std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex);
diff --git a/services/gpuservice/vts/TEST_MAPPING b/services/gpuservice/vts/TEST_MAPPING
index b33e962..a809be1 100644
--- a/services/gpuservice/vts/TEST_MAPPING
+++ b/services/gpuservice/vts/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit": [
{
- "name": "GpuServiceVendorTests"
+ "name": "GpuServiceVendorTests",
+ "options": [
+ {
+ // Exclude test methods that require a physical device to run.
+ "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
}
]
}
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 6c16335..5c12323 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
-import android.platform.test.annotations.RestrictedBuildTest;
+import android.platform.test.annotations.RequiresDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -63,7 +63,7 @@
}
@VsrTest(requirements={"VSR-3.3-004"})
- @RestrictedBuildTest
+ @RequiresDevice
@Test
public void testGpuWorkPeriodTracepointFormat() throws Exception {
CommandResult commandResult = getDevice().executeShellV2Command(
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 70801dc..ca92ab5 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -172,9 +172,8 @@
export_static_lib_headers: [
"libinputdispatcher",
],
- export_include_dirs: [
- ".",
- "include",
+ export_shared_lib_headers: [
+ "libinputflinger_base",
],
}
@@ -185,7 +184,16 @@
cc_library_headers {
name: "libinputflinger_headers",
host_supported: true,
- export_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ ".",
+ ],
+ header_libs: [
+ "libchrome-gestures_headers",
+ ],
+ export_header_lib_headers: [
+ "libchrome-gestures_headers",
+ ],
}
filegroup {
@@ -209,6 +217,7 @@
"libcutils",
"libinput",
"liblog",
+ "libprocessgroup",
"libstatslog",
"libutils",
],
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index b5cb3cb..4621144 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -133,15 +133,6 @@
mNextListener.notify(args);
}
-void InputDeviceMetricsCollector::notifyConfigurationChanged(
- const NotifyConfigurationChangedArgs& args) {
- {
- std::scoped_lock lock(mLock);
- reportCompletedSessions();
- }
- mNextListener.notify(args);
-}
-
void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
{
std::scoped_lock lock(mLock);
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 1bcd527..0a520e6 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -107,7 +107,6 @@
std::chrono::nanoseconds usageSessionTimeout);
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 8e73ce5..2ef94fb 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -60,6 +60,7 @@
AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back();
aidlInfo.deviceId = info.getId();
aidlInfo.external = info.isExternal();
+ aidlInfo.keyboardType = info.getKeyboardType();
}
if (isFilterEnabled()) {
LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk());
@@ -67,10 +68,6 @@
mNextListener.notify(args);
}
-void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- mNextListener.notify(args);
-}
-
void InputFilter::notifyKey(const NotifyKeyArgs& args) {
if (isFilterEnabled()) {
LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
@@ -149,6 +146,12 @@
void InputFilter::dump(std::string& dump) {
dump += "InputFilter:\n";
+ if (isFilterEnabled()) {
+ std::string result;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->dumpFilter(&result).isOk());
+ dump += result;
+ dump += "\n";
+ }
}
} // namespace android
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index 4ddc9f4..f626703 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -53,7 +53,6 @@
InputFilterPolicyInterface& policy);
~InputFilter() override = default;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 016ae04..8b6accf 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -47,7 +47,6 @@
void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
Visitor v{
[&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },
- [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },
[&](const NotifyKeyArgs& args) { notifyKey(args); },
[&](const NotifyMotionArgs& args) { notifyMotion(args); },
[&](const NotifySwitchArgs& args) { notifySwitch(args); },
@@ -68,10 +67,6 @@
mArgsQueue.emplace_back(args);
}
-void QueuedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- mArgsQueue.emplace_back(args);
-}
-
void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) {
mArgsQueue.emplace_back(args);
}
@@ -119,13 +114,6 @@
mInnerListener.notify(args);
}
-void TracedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- constexpr static auto& fnName = __func__;
- ATRACE_NAME_IF(ATRACE_ENABLED(),
- StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id));
- mInnerListener.notify(args);
-}
-
void TracedInputListener::notifyKey(const NotifyKeyArgs& args) {
constexpr static auto& fnName = __func__;
ATRACE_NAME_IF(ATRACE_ENABLED(),
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 41e5247..b155122 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -250,6 +250,10 @@
mCollector->dump(dump);
dump += '\n';
}
+ if (ENABLE_INPUT_FILTER_RUST) {
+ mInputFilter->dump(dump);
+ dump += '\n';
+ }
mDispatcher->dump(dump);
dump += '\n';
}
diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp
index 6dd267c..8b8b1ad 100644
--- a/services/inputflinger/InputProcessor.cpp
+++ b/services/inputflinger/InputProcessor.cpp
@@ -419,12 +419,6 @@
mQueuedListener.flush();
}
-void InputProcessor::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- // pass through
- mQueuedListener.notifyConfigurationChanged(args);
- mQueuedListener.flush();
-}
-
void InputProcessor::notifyKey(const NotifyKeyArgs& args) {
// pass through
mQueuedListener.notifyKey(args);
diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h
index 7a00a2d..2945dd2 100644
--- a/services/inputflinger/InputProcessor.h
+++ b/services/inputflinger/InputProcessor.h
@@ -246,7 +246,6 @@
explicit InputProcessor(InputListenerInterface& listener);
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
index e74f258..449eb45 100644
--- a/services/inputflinger/InputThread.cpp
+++ b/services/inputflinger/InputThread.cpp
@@ -16,8 +16,14 @@
#include "InputThread.h"
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <processgroup/processgroup.h>
+
namespace android {
+namespace input_flags = com::android::input::flags;
+
namespace {
// Implementation of Thread from libutils.
@@ -43,6 +49,11 @@
: mName(name), mThreadWake(wake) {
mThread = sp<InputThreadImpl>::make(loop);
mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+ if (input_flags::enable_input_policy_profile()) {
+ if (!applyInputEventProfile()) {
+ LOG(ERROR) << "Couldn't apply input policy profile for " << name;
+ }
+ }
}
InputThread::~InputThread() {
@@ -63,4 +74,14 @@
#endif
}
+bool InputThread::applyInputEventProfile() {
+#if defined(__ANDROID__)
+ return SetTaskProfiles(mThread->getTid(), {"InputPolicy"});
+#else
+ // Since thread information is not available and there's no benefit of
+ // applying the task profile on host, return directly.
+ return true;
+#endif
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 19a4f26..b2680a2 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -35,11 +35,6 @@
std::vector<InputDeviceInfo> infos)
: id(id), inputDeviceInfos(std::move(infos)) {}
-// --- NotifyConfigurationChangedArgs ---
-
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
- : id(id), eventTime(eventTime) {}
-
// --- NotifyKeyArgs ---
NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
@@ -198,7 +193,6 @@
const char* toString(const NotifyArgs& args) {
Visitor toStringVisitor{
[&](const NotifyInputDevicesChangedArgs&) { return "NotifyInputDevicesChangedArgs"; },
- [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; },
[&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; },
[&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; },
[&](const NotifySensorArgs&) { return "NotifySensorArgs"; },
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index ed77146..006d507 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -165,10 +165,6 @@
mNextListener.notify(args);
}
-void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- mNextListener.notify(args);
-}
-
void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
fadeMouseCursorOnKeyPress(args);
mNextListener.notify(args);
@@ -202,6 +198,7 @@
}
auto it = mMousePointersByDisplay.find(targetDisplay);
if (it != mMousePointersByDisplay.end()) {
+ mPolicy.notifyMouseCursorFadedOnTyping();
it->second->fade(PointerControllerInterface::Transition::GRADUAL);
}
}
@@ -410,7 +407,8 @@
// TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
// immediately by a DOWN event.
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
- pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
+ pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER
+ : PointerIconStyle::TYPE_NOT_SPECIFIED);
} else if (canUnfadeOnDisplay(args.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
@@ -514,8 +512,9 @@
std::scoped_lock _l(mLock);
dump += "PointerChoreographer:\n";
- dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
- dump += StringPrintf("stylus pointer icon enabled: %s\n",
+ dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
+ mShowTouchesEnabled ? "true" : "false");
+ dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
mStylusPointerIconEnabled ? "true" : "false");
dump += INDENT "MousePointerControllers:\n";
@@ -794,6 +793,13 @@
if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
auto it = mStylusPointersByDevice.find(deviceId);
if (it != mStylusPointersByDevice.end()) {
+ if (mShowTouchesEnabled) {
+ // If an app doesn't override the icon for the hovering stylus, show the hover icon.
+ auto* style = std::get_if<PointerIconStyle>(&icon);
+ if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) {
+ *style = PointerIconStyle::TYPE_SPOT_HOVER;
+ }
+ }
setIconForController(icon, *it->second);
return true;
}
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index aaf1e3e..635487b 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -105,7 +105,6 @@
void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 2ebe708..43eaf67 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -74,6 +74,9 @@
},
{
"name": "monkey_test"
+ },
+ {
+ "name": "CtsSurfaceControlTests"
}
],
"postsubmit": [
@@ -143,11 +146,19 @@
},
{
"name": "monkey_test"
+ },
+ {
+ "name": "CtsInputRootTestCases"
+ }
+ ],
+ "platinum-postsubmit": [
+ {
+ "name": "inputflinger_tests"
}
],
"staged-platinum-postsubmit": [
{
- "name": "inputflinger_tests"
+ "name": "libinput_tests"
}
]
}
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 1e2b9b3a..0e9ec91 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -342,12 +342,6 @@
bool enablePalmRejection)
: mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {}
-void UnwantedInteractionBlocker::notifyConfigurationChanged(
- const NotifyConfigurationChangedArgs& args) {
- mQueuedListener.notifyConfigurationChanged(args);
- mQueuedListener.flush();
-}
-
void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs& args) {
mQueuedListener.notifyKey(args);
mQueuedListener.flush();
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index 419da83..8a66e25 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -91,7 +91,6 @@
explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection);
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
index b9e6a03..5b0b9ac 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
@@ -23,4 +23,5 @@
parcelable DeviceInfo {
int deviceId;
boolean external;
+ int keyboardType;
}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 994d1c4..31b7231 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -54,5 +54,7 @@
/** Notifies when configuration changes */
void notifyConfigurationChanged(in InputFilterConfiguration config);
+
+ String dumpFilter();
}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index ad9cec1..ff407af 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -68,15 +68,6 @@
injectionState(nullptr),
dispatchInProgress(false) {}
-// --- ConfigurationChangedEntry ---
-
-ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime)
- : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
-
-std::string ConfigurationChangedEntry::getDescription() const {
- return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
-}
-
// --- DeviceResetEntry ---
DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId)
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index f2f31d8..becfb05 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -32,7 +32,6 @@
struct EventEntry {
enum class Type {
- CONFIGURATION_CHANGED,
DEVICE_RESET,
FOCUS,
KEY,
@@ -78,11 +77,6 @@
virtual ~EventEntry() = default;
};
-struct ConfigurationChangedEntry : EventEntry {
- explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
- std::string getDescription() const override;
-};
-
struct DeviceResetEntry : EventEntry {
int32_t deviceId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7eb7e36..5db21fd 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -52,6 +52,7 @@
#include "Connection.h"
#include "DebugConfig.h"
#include "InputDispatcher.h"
+#include "InputEventTimeline.h"
#include "trace/InputTracer.h"
#include "trace/InputTracingPerfettoBackend.h"
#include "trace/ThreadedBackend.h"
@@ -558,7 +559,6 @@
// Returns true if the event type passed as argument represents a user activity.
bool isUserActivityEvent(const EventEntry& eventEntry) {
switch (eventEntry.type) {
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::DRAG:
case EventEntry::Type::FOCUS:
@@ -694,7 +694,8 @@
*/
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
const TouchState& newTouchState,
- const MotionEntry& entry) {
+ const MotionEntry& entry,
+ std::function<void()> dump) {
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
@@ -742,6 +743,7 @@
// crashing the device with FATAL.
severity = android::base::LogSeverity::ERROR;
}
+ dump();
LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
}
touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
@@ -904,6 +906,24 @@
const nsecs_t mProcessingTimestamp;
};
+/**
+ * This is needed to help use "InputEventInjectionResult" with base::Result.
+ */
+template <typename T>
+struct EnumErrorWrapper {
+ T mVal;
+ EnumErrorWrapper(T&& e) : mVal(std::forward<T>(e)) {}
+ operator const T&() const { return mVal; }
+ T value() const { return mVal; }
+ std::string print() const { return ftl::enum_string(mVal); }
+};
+
+Error<EnumErrorWrapper<InputEventInjectionResult>> injectionError(InputEventInjectionResult&& e) {
+ LOG_ALWAYS_FATAL_IF(e == InputEventInjectionResult::SUCCEEDED);
+ return Error<EnumErrorWrapper<InputEventInjectionResult>>(
+ std::forward<InputEventInjectionResult>(e));
+}
+
} // namespace
// --- InputDispatcher ---
@@ -1156,14 +1176,6 @@
}
switch (mPendingEvent->type) {
- case EventEntry::Type::CONFIGURATION_CHANGED: {
- const ConfigurationChangedEntry& typedEntry =
- static_cast<const ConfigurationChangedEntry&>(*mPendingEvent);
- done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
- dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
- break;
- }
-
case EventEntry::Type::DEVICE_RESET: {
const DeviceResetEntry& typedEntry =
static_cast<const DeviceResetEntry&>(*mPendingEvent);
@@ -1311,7 +1323,7 @@
// Alternatively, maybe there's a spy window that could handle this event.
const std::vector<sp<WindowInfoHandle>> touchedSpies =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
+ findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, motionEntry.deviceId);
for (const auto& windowHandle : touchedSpies) {
const std::shared_ptr<Connection> connection =
getConnectionLocked(windowHandle->getToken());
@@ -1395,7 +1407,6 @@
break;
}
case EventEntry::Type::TOUCH_MODE_CHANGED:
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
@@ -1466,15 +1477,27 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const {
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
-
if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
- continue;
+ // Generally, we would skip any pointer that's outside of the window. However, if the
+ // spy prevents splitting, and already has some of the pointers from this device, then
+ // it should get more pointers from the same device, even if they are outside of that
+ // window
+ if (info.supportsSplitTouch()) {
+ continue;
+ }
+
+ // We know that split touch is not supported. Skip this window only if it doesn't have
+ // any touching pointers for this device already.
+ if (!windowHasTouchingPointersLocked(windowHandle, deviceId)) {
+ continue;
+ }
+ // If it already has pointers down for this device, then give it this pointer, too.
}
if (!info.isSpy()) {
// The first touched non-spy window was found, so return the spy windows touched so far.
@@ -1557,7 +1580,6 @@
}
case EventEntry::Type::FOCUS:
case EventEntry::Type::TOUCH_MODE_CHANGED:
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str());
break;
@@ -1646,24 +1668,6 @@
return newEntry;
}
-bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
- const ConfigurationChangedEntry& entry) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
- }
-
- // Reset key repeating in case a keyboard device was added or removed or something.
- resetKeyRepeatLocked();
-
- // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
- auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
- scoped_unlock unlock(mLock);
- mPolicy.notifyConfigurationChanged(eventTime);
- };
- postCommandLocked(std::move(command));
- return true;
-}
-
bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
const DeviceResetEntry& entry) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
@@ -1926,20 +1930,21 @@
}
// Identify targets.
- InputEventInjectionResult injectionResult;
- sp<WindowInfoHandle> focusedWindow =
- findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime,
- /*byref*/ injectionResult);
- if (injectionResult == InputEventInjectionResult::PENDING) {
- return false;
- }
+ Result<sp<WindowInfoHandle>, InputEventInjectionResult> result =
+ findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime);
- setInjectionResult(*entry, injectionResult);
- if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
+ if (!result.ok()) {
+ if (result.error().code() == InputEventInjectionResult::PENDING) {
+ return false;
+ }
+ setInjectionResult(*entry, result.error().code());
return true;
}
+ sp<WindowInfoHandle>& focusedWindow = *result;
LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
+ setInjectionResult(*entry, InputEventInjectionResult::SUCCEEDED);
+
std::vector<InputTarget> inputTargets;
addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
InputTarget::Flags::FOREGROUND, getDownTime(*entry), inputTargets);
@@ -2044,19 +2049,28 @@
pilferPointersLocked(mDragState->dragWindow->getToken());
}
- inputTargets =
- findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult);
- LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
- !inputTargets.empty());
+ Result<std::vector<InputTarget>, InputEventInjectionResult> result =
+ findTouchedWindowTargetsLocked(currentTime, *entry);
+
+ if (result.ok()) {
+ inputTargets = std::move(*result);
+ injectionResult = InputEventInjectionResult::SUCCEEDED;
+ } else {
+ injectionResult = result.error().code();
+ }
} else {
// Non touch event. (eg. trackball)
- sp<WindowInfoHandle> focusedWindow =
- findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult);
- if (injectionResult == InputEventInjectionResult::SUCCEEDED) {
+ Result<sp<WindowInfoHandle>, InputEventInjectionResult> result =
+ findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime);
+ if (result.ok()) {
+ sp<WindowInfoHandle>& focusedWindow = *result;
LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
InputTarget::Flags::FOREGROUND, getDownTime(*entry),
inputTargets);
+ injectionResult = InputEventInjectionResult::SUCCEEDED;
+ } else {
+ injectionResult = result.error().code();
}
}
if (injectionResult == InputEventInjectionResult::PENDING) {
@@ -2117,19 +2131,16 @@
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%s, policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, "
- "metaState=0x%x, buttonState=0x%x,"
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+ "metaState=0x%x, buttonState=0x%x, downTime=%" PRId64,
prefix, entry.eventTime, entry.deviceId,
inputEventSourceToString(entry.source).c_str(), entry.displayId.toString().c_str(),
entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
- entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
- entry.xPrecision, entry.yPrecision, entry.downTime);
+ entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.downTime);
for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, "
"x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
i, entry.pointerProperties[i].id,
ftl::enum_string(entry.pointerProperties[i].toolType).c_str(),
entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
@@ -2224,7 +2235,6 @@
case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::FOCUS:
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
case EventEntry::Type::DRAG: {
@@ -2266,11 +2276,9 @@
return false;
}
-sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
- nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
- InputEventInjectionResult& outInjectionResult) {
- outInjectionResult = InputEventInjectionResult::FAILED; // Default result
-
+Result<sp<WindowInfoHandle>, InputEventInjectionResult>
+InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry,
+ nsecs_t& nextWakeupTime) {
ui::LogicalDisplayId displayId = getTargetDisplayId(entry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
@@ -2282,12 +2290,12 @@
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %s.",
ftl::enum_string(entry.type).c_str(), displayId.toString().c_str());
- return nullptr;
+ return injectionError(InputEventInjectionResult::FAILED);
}
// Drop key events if requested by input feature
if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
- return nullptr;
+ return injectionError(InputEventInjectionResult::FAILED);
}
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -2307,17 +2315,15 @@
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime);
- outInjectionResult = InputEventInjectionResult::PENDING;
- return nullptr;
+ return injectionError(InputEventInjectionResult::PENDING);
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
ftl::enum_string(entry.type).c_str());
- return nullptr;
+ return injectionError(InputEventInjectionResult::FAILED);
} else {
// Still waiting for the focused window
- outInjectionResult = InputEventInjectionResult::PENDING;
- return nullptr;
+ return injectionError(InputEventInjectionResult::PENDING);
}
}
@@ -2327,15 +2333,13 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
- outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
- return nullptr;
+ return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
if (focusedWindowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
- outInjectionResult = InputEventInjectionResult::PENDING;
- return nullptr;
+ return injectionError(InputEventInjectionResult::PENDING);
}
// If the event is a key event, then we must wait for all previous events to
@@ -2352,12 +2356,10 @@
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout);
- outInjectionResult = InputEventInjectionResult::PENDING;
- return nullptr;
+ return injectionError(InputEventInjectionResult::PENDING);
}
}
-
- outInjectionResult = InputEventInjectionResult::SUCCEEDED;
+ // Success!
return focusedWindowHandle;
}
@@ -2381,9 +2383,8 @@
return responsiveMonitors;
}
-std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry,
- InputEventInjectionResult& outInjectionResult) {
+base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
+InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) {
ATRACE_CALL();
std::vector<InputTarget> targets;
@@ -2393,9 +2394,6 @@
const int32_t action = entry.action;
const int32_t maskedAction = MotionEvent::getActionMasked(action);
- // Update the touch state as needed based on the properties of the touch event.
- outInjectionResult = InputEventInjectionResult::PENDING;
-
// Copy current touch state into tempTouchState.
// This state will be used to update mTouchStatesByDisplay at the end of this function.
// If no state for the specified display exists, then our initial state will be empty.
@@ -2435,8 +2433,7 @@
// Started hovering, but the device is already down: reject the hover event
LOG(ERROR) << "Got hover event " << entry.getDescription()
<< " but the device is already down " << oldState->dump();
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
+ return injectionError(InputEventInjectionResult::FAILED);
}
// For hover actions, we will treat 'tempTouchState' as a new state, so let's erase
// all of the existing hovering pointers and recompute.
@@ -2457,20 +2454,25 @@
if (isDown) {
targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
}
+ LOG_IF(INFO, newTouchedWindowHandle == nullptr)
+ << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
+ << ") in display " << displayId;
// Handle the case where we did not find a window.
- if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%.1f, %.1f) in display %s", x, y,
- displayId.toString().c_str());
- // Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
+ if (!input_flags::split_all_touches()) {
+ // If we are force splitting all touches, then touches outside of the window should
+ // be dropped, even if this device already has pointers down in another window.
+ if (newTouchedWindowHandle == nullptr) {
+ // Try to assign the pointer to the first foreground window we find, if there is
+ // one.
+ newTouchedWindowHandle =
+ tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
+ }
}
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected touch event: %s", (*err).c_str());
- outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
- newTouchedWindowHandle = nullptr;
- return {};
+ return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
// Figure out whether splitting will be allowed for this window.
@@ -2493,18 +2495,16 @@
}
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
+ findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, entry.deviceId);
if (newTouchedWindowHandle != nullptr) {
// Process the foreground window first so that it is the first to receive the event.
newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
}
if (newTouchedWindows.empty()) {
- ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
- "%s.",
- x, y, displayId.toString().c_str());
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
+ LOG(INFO) << "Dropping event because there is no touchable window at (" << x << ", "
+ << y << ") on display " << displayId << ": " << entry;
+ return injectionError(InputEventInjectionResult::FAILED);
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
@@ -2605,8 +2605,7 @@
<< " is not down or we previously dropped the pointer down event in "
<< "display " << displayId << ": " << entry.getDescription();
}
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
+ return injectionError(InputEventInjectionResult::FAILED);
}
// If the pointer is not currently hovering, then ignore the event.
@@ -2617,8 +2616,7 @@
LOG(INFO) << "Dropping event because the hovering pointer is not in any windows in "
"display "
<< displayId << ": " << entry.getDescription();
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
+ return injectionError(InputEventInjectionResult::FAILED);
}
tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
}
@@ -2639,8 +2637,7 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
- outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
- return {};
+ return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
// Do not slide events to the window which can not receive motion event
@@ -2693,7 +2690,7 @@
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry.deviceId, pointer, targets);
+ tempTouchState, entry, targets);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -2720,7 +2717,9 @@
// Update dispatching for hover enter and exit.
{
std::vector<TouchedWindow> hoveringWindows =
- getHoveringWindowsLocked(oldState, tempTouchState, entry);
+ getHoveringWindowsLocked(oldState, tempTouchState, entry,
+ std::bind_front(&InputDispatcher::logDispatchStateLocked,
+ this));
// Hardcode to single hovering pointer for now.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(entry.pointerProperties[0].id);
@@ -2743,8 +2742,7 @@
ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
"%s:%s",
entry.injectionState->targetUid->toString().c_str(), errs.c_str());
- outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
- return {};
+ return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
}
@@ -2801,8 +2799,7 @@
if (targets.empty()) {
LOG(INFO) << "Dropping event because no targets were found: " << entry.getDescription();
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
+ return injectionError(InputEventInjectionResult::FAILED);
}
// If we only have windows getting ACTION_OUTSIDE, then drop the event, because there is no
@@ -2812,12 +2809,9 @@
})) {
LOG(INFO) << "Dropping event because all windows would just receive ACTION_OUTSIDE: "
<< entry.getDescription();
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
+ return injectionError(InputEventInjectionResult::FAILED);
}
- outInjectionResult = InputEventInjectionResult::SUCCEEDED;
-
// Now that we have generated all of the input targets for this event, reset the dispatch
// mode for all touched window to AS_IS.
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
@@ -3626,7 +3620,6 @@
LOG_ALWAYS_FATAL("SENSOR events should not go to apps via input channel");
break;
}
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
ftl::enum_string(eventEntry->type).c_str());
@@ -3839,6 +3832,10 @@
}
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
status = publishMotionEvent(*connection, *dispatchEntry);
+ if (status == BAD_VALUE) {
+ logDispatchStateLocked();
+ LOG(FATAL) << "Publisher failed for " << motionEntry;
+ }
if (mTracer) {
ensureEventTraced(motionEntry);
mTracer->traceEventDispatch(*dispatchEntry, *motionEntry.traceTracker);
@@ -3883,7 +3880,6 @@
break;
}
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
@@ -4279,7 +4275,6 @@
ftl::enum_string(cancelationEventEntry->type).c_str());
break;
}
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
@@ -4362,7 +4357,6 @@
case EventEntry::Type::KEY:
case EventEntry::Type::FOCUS:
case EventEntry::Type::TOUCH_MODE_CHANGED:
- case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::SENSOR:
@@ -4448,28 +4442,11 @@
void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
std::scoped_lock _l(mLock);
+ // Reset key repeating in case a keyboard device was added or removed or something.
+ resetKeyRepeatLocked();
mLatencyTracker.setInputDevices(args.inputDeviceInfos);
}
-void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime);
- }
-
- bool needWake = false;
- { // acquire lock
- std::scoped_lock _l(mLock);
-
- std::unique_ptr<ConfigurationChangedEntry> newEntry =
- std::make_unique<ConfigurationChangedEntry>(args.id, args.eventTime);
- needWake = enqueueInboundEventLocked(std::move(newEntry));
- } // release lock
-
- if (needWake) {
- mLooper->wake();
- }
-}
-
void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
ALOGD_IF(debugInboundEventDetails(),
"notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
@@ -4519,6 +4496,10 @@
{ // acquire lock
mLock.lock();
+ if (input_flags::keyboard_repeat_keys() && !mConfig.keyRepeatEnabled) {
+ policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+ }
+
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
@@ -4557,13 +4538,12 @@
ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
"displayId=%s, policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
- "yCursorPosition=%f, downTime=%" PRId64,
+ "xCursorPosition=%f, yCursorPosition=%f, downTime=%" PRId64,
args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
args.displayId.toString().c_str(), args.policyFlags,
MotionEvent::actionToString(args.action).c_str(), args.actionButton, args.flags,
- args.metaState, args.buttonState, args.edgeFlags, args.xPrecision, args.yPrecision,
- args.xCursorPosition, args.yCursorPosition, args.downTime);
+ args.metaState, args.buttonState, args.xCursorPosition, args.yCursorPosition,
+ args.downTime);
for (uint32_t i = 0; i < args.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
@@ -4672,10 +4652,9 @@
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
- const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
- mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime,
- args.deviceId, sources);
+ mLatencyTracker.trackListener(args.id, args.eventTime, args.readTime, args.deviceId,
+ sources, args.action, InputEventType::MOTION);
}
needWake = enqueueInboundEventLocked(std::move(newEntry));
@@ -4913,6 +4892,10 @@
logDispatchStateLocked();
LOG(ERROR) << "Inconsistent event: " << motionEvent
<< ", reason: " << result.error();
+ if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+ mLock.unlock();
+ return InputEventInjectionResult::FAILED;
+ }
}
}
@@ -5459,7 +5442,8 @@
for (DeviceId deviceId : erasedDevices) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
- "WindowInfo changed", traceContext.getTracker());
+ "WindowInfo changed",
+ traceContext.getTracker());
options.deviceId = deviceId;
synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
}
@@ -5710,7 +5694,7 @@
mMaximumObscuringOpacityForTouch = opacity;
}
-std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/>
+std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) {
for (auto& [displayId, state] : mTouchStatesByDisplay) {
for (TouchedWindow& w : state.windows) {
@@ -5722,6 +5706,22 @@
return std::make_tuple(nullptr, nullptr, ui::LogicalDisplayId::DEFAULT);
}
+std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
+InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const {
+ return const_cast<InputDispatcher*>(this)->findTouchStateWindowAndDisplayLocked(token);
+}
+
+bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle,
+ DeviceId deviceId) const {
+ const auto& [touchState, touchedWindow, _] =
+ findTouchStateWindowAndDisplayLocked(windowHandle->getToken());
+ if (touchState == nullptr) {
+ // No touching pointers at all
+ return false;
+ }
+ return touchState->hasTouchingPointers(deviceId);
+}
+
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) {
if (fromToken == toToken) {
@@ -6058,17 +6058,12 @@
dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
connection->outboundQueue.size());
dump += dumpQueue(connection->outboundQueue, currentTime);
-
- } else {
- dump += INDENT3 "OutboundQueue: <empty>\n";
}
if (!connection->waitQueue.empty()) {
dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
connection->waitQueue.size());
dump += dumpQueue(connection->waitQueue, currentTime);
- } else {
- dump += INDENT3 "WaitQueue: <empty>\n";
}
std::string inputStateDump = streamableToString(connection->inputState);
if (!inputStateDump.empty()) {
@@ -7062,6 +7057,13 @@
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
+ if (input_flags::split_all_touches()) {
+ handlesPerDisplay[info.displayId]
+ .back()
+ ->editInfo()
+ ->setInputConfig(android::gui::WindowInfo::InputConfig::PREVENT_SPLITTING,
+ false);
+ }
}
{ // acquire lock
@@ -7137,9 +7139,11 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const {
+ LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
+ const DeviceId deviceId = entry.deviceId;
+ const PointerProperties& pointerProperties = entry.pointerProperties[0];
std::vector<PointerProperties> pointers{pointerProperties};
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7166,7 +7170,7 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointers);
+ deviceId, pointers, entry.eventTime);
}
}
@@ -7240,11 +7244,13 @@
}
void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
- std::chrono::nanoseconds delay) {
+ std::chrono::nanoseconds delay,
+ bool keyRepeatEnabled) {
std::scoped_lock _l(mLock);
mConfig.keyRepeatTimeout = timeout.count();
mConfig.keyRepeatDelay = delay.count();
+ mConfig.keyRepeatEnabled = keyRepeatEnabled;
}
bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e2fc7a0..1904058 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -97,7 +97,6 @@
status_t stop() override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
void notifySwitch(const NotifySwitchArgs& args) override;
@@ -154,8 +153,8 @@
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
- void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
- std::chrono::nanoseconds delay) override;
+ void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, std::chrono::nanoseconds delay,
+ bool keyRepeatEnabled) override;
bool isPointerInWindow(const sp<IBinder>& token, ui::LogicalDisplayId displayId,
DeviceId deviceId, int32_t pointerId) override;
@@ -258,7 +257,8 @@
int32_t pointerId) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus,
+ DeviceId deviceId) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(
ui::LogicalDisplayId displayId) const REQUIRES(mLock);
@@ -446,8 +446,6 @@
REQUIRES(mLock);
// Dispatch inbound events.
- bool dispatchConfigurationChangedLocked(nsecs_t currentTime,
- const ConfigurationChangedEntry& entry) REQUIRES(mLock);
bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
REQUIRES(mLock);
bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
@@ -536,12 +534,11 @@
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
ui::LogicalDisplayId getTargetDisplayId(const EventEntry& entry);
- sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
- nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
- android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
- std::vector<InputTarget> findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry,
- android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
+ base::Result<sp<android::gui::WindowInfoHandle>, android::os::InputEventInjectionResult>
+ findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry,
+ nsecs_t& nextWakeupTime) REQUIRES(mLock);
+ base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
+ findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) REQUIRES(mLock);
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
@@ -684,16 +681,21 @@
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
- std::map<ui::LogicalDisplayId /*displayId*/, InputVerifier> mVerifiersByDisplay;
+ std::map<ui::LogicalDisplayId, InputVerifier> mVerifiersByDisplay;
// Returns a fallback KeyEntry that should be sent to the connection, if required.
std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
- std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/>
+ std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock);
+ std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
+ findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const REQUIRES(mLock);
+ bool windowHasTouchingPointersLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ DeviceId deviceId) const REQUIRES(mLock);
+
// Statistics gathering.
LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
LatencyTracker mLatencyTracker GUARDED_BY(mLock);
@@ -707,11 +709,23 @@
sp<InputReporterInterface> mReporter;
+ /**
+ * Slip the wallpaper touch if necessary.
+ *
+ * @param targetFlags the target flags
+ * @param oldWindowHandle the old window that the touch slipped out of
+ * @param newWindowHandle the new window that the touch is slipping into
+ * @param state the current touch state. This will be updated if necessary to reflect the new
+ * windows that are receiving touch.
+ * @param deviceId the device id of the current motion being processed
+ * @param pointerProperties the pointer properties of the current motion being processed
+ * @param targets the current targets to add the walpaper ones to
+ * @param eventTime the new downTime for the wallpaper target
+ */
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
index a7c6d16..6881964 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.cpp
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -66,15 +66,16 @@
return !operator==(rhs);
}
-InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime,
- uint16_t vendorId, uint16_t productId,
- std::set<InputDeviceUsageSource> sources)
- : isDown(isDown),
- eventTime(eventTime),
+InputEventTimeline::InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
+ uint16_t productId,
+ const std::set<InputDeviceUsageSource>& sources,
+ InputEventActionType inputEventActionType)
+ : eventTime(eventTime),
readTime(readTime),
vendorId(vendorId),
productId(productId),
- sources(sources) {}
+ sources(sources),
+ inputEventActionType(inputEventActionType) {}
bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const {
if (connectionTimelines.size() != rhs.connectionTimelines.size()) {
@@ -89,8 +90,9 @@
return false;
}
}
- return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime &&
- vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources;
+ return eventTime == rhs.eventTime && readTime == rhs.readTime && vendorId == rhs.vendorId &&
+ productId == rhs.productId && sources == rhs.sources &&
+ inputEventActionType == rhs.inputEventActionType;
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index e9deb2d..951fcc8 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -74,15 +74,38 @@
bool mHasGraphicsTimeline = false;
};
+enum class InputEventActionType : int32_t {
+ UNKNOWN_INPUT_EVENT = 0,
+ MOTION_ACTION_DOWN = 1,
+ // Motion events for ACTION_MOVE (characterizes scrolling motion)
+ MOTION_ACTION_MOVE = 2,
+ // Motion events for ACTION_UP (when the pointer first goes up)
+ MOTION_ACTION_UP = 3,
+ // Motion events for ACTION_HOVER_MOVE (pointer position on screen changes but pointer is not
+ // down)
+ MOTION_ACTION_HOVER_MOVE = 4,
+ // Motion events for ACTION_SCROLL (moving the mouse wheel)
+ MOTION_ACTION_SCROLL = 5,
+ // Key events for both ACTION_DOWN and ACTION_UP (key press and key release)
+ KEY = 6,
+
+ ftl_first = UNKNOWN_INPUT_EVENT,
+ ftl_last = KEY,
+ // Used by latency fuzzer
+ kMaxValue = ftl_last
+
+};
+
struct InputEventTimeline {
- InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
- uint16_t productId, std::set<InputDeviceUsageSource> sources);
- const bool isDown; // True if this is an ACTION_DOWN event
+ InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, uint16_t productId,
+ const std::set<InputDeviceUsageSource>& sources,
+ InputEventActionType inputEventActionType);
const nsecs_t eventTime;
const nsecs_t readTime;
const uint16_t vendorId;
const uint16_t productId;
const std::set<InputDeviceUsageSource> sources;
+ const InputEventActionType inputEventActionType;
struct IBinderHash {
std::size_t operator()(const sp<IBinder>& b) const {
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index e283fc3..9b5a79b 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -649,7 +649,9 @@
if (!state.mMotionMementos.empty()) {
out << "mMotionMementos: ";
for (const InputState::MotionMemento& memento : state.mMotionMementos) {
- out << "{deviceId= " << memento.deviceId << ", hovering=" << memento.hovering << "}, ";
+ out << "{deviceId=" << memento.deviceId
+ << ", hovering=" << std::to_string(memento.hovering)
+ << ", downTime=" << memento.downTime << "}, ";
}
}
return out;
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp
index e09d97a..4ddd2e9 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.cpp
+++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp
@@ -134,7 +134,9 @@
mNumSketchEventsProcessed++;
std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches =
- timeline.isDown ? mDownSketches : mMoveSketches;
+ timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN
+ ? mDownSketches
+ : mMoveSketches;
// Process common ones first
const nsecs_t eventToRead = timeline.readTime - timeline.eventTime;
@@ -242,7 +244,9 @@
const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime;
const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime;
- android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, timeline.isDown,
+ android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED,
+ timeline.inputEventActionType ==
+ InputEventActionType::MOTION_ACTION_DOWN,
static_cast<int32_t>(ns2us(eventToRead)),
static_cast<int32_t>(ns2us(readToDeliver)),
static_cast<int32_t>(ns2us(deliverToConsume)),
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 698bd9f..69024b3 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -67,9 +67,10 @@
LOG_ALWAYS_FATAL_IF(processor == nullptr);
}
-void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime,
- nsecs_t readTime, DeviceId deviceId,
- const std::set<InputDeviceUsageSource>& sources) {
+void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime,
+ DeviceId deviceId,
+ const std::set<InputDeviceUsageSource>& sources,
+ int32_t inputEventAction, InputEventType inputEventType) {
reportAndPruneMatureRecords(eventTime);
const auto it = mTimelines.find(inputEventId);
if (it != mTimelines.end()) {
@@ -101,9 +102,41 @@
return;
}
+ const InputEventActionType inputEventActionType = [&]() {
+ switch (inputEventType) {
+ case InputEventType::MOTION: {
+ switch (MotionEvent::getActionMasked(inputEventAction)) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return InputEventActionType::MOTION_ACTION_DOWN;
+ case AMOTION_EVENT_ACTION_MOVE:
+ return InputEventActionType::MOTION_ACTION_MOVE;
+ case AMOTION_EVENT_ACTION_UP:
+ return InputEventActionType::MOTION_ACTION_UP;
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ return InputEventActionType::MOTION_ACTION_HOVER_MOVE;
+ case AMOTION_EVENT_ACTION_SCROLL:
+ return InputEventActionType::MOTION_ACTION_SCROLL;
+ default:
+ return InputEventActionType::UNKNOWN_INPUT_EVENT;
+ }
+ }
+ case InputEventType::KEY: {
+ switch (inputEventAction) {
+ case AKEY_EVENT_ACTION_DOWN:
+ case AKEY_EVENT_ACTION_UP:
+ return InputEventActionType::KEY;
+ default:
+ return InputEventActionType::UNKNOWN_INPUT_EVENT;
+ }
+ }
+ default:
+ return InputEventActionType::UNKNOWN_INPUT_EVENT;
+ }
+ }();
+
mTimelines.emplace(inputEventId,
- InputEventTimeline(isDown, eventTime, readTime, identifier->vendor,
- identifier->product, sources));
+ InputEventTimeline(eventTime, readTime, identifier->vendor,
+ identifier->product, sources, inputEventActionType));
mEventTimes.emplace(eventTime, inputEventId);
}
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 890d61d..b4053ba 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -52,8 +52,9 @@
* duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
* must drop all duplicate data.
*/
- void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime,
- DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources);
+ void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
+ const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
+ InputEventType inputEventType);
void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 3fbe584..451d917 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,7 @@
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
+ std::optional<nsecs_t> firstDownTimeInTarget);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer, float x,
float y);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
index 5eb3a32..ba197d4 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
@@ -34,8 +34,13 @@
// The key repeat inter-key delay.
nsecs_t keyRepeatDelay;
+ // Whether key repeat is enabled.
+ bool keyRepeatEnabled;
+
InputDispatcherConfiguration()
- : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {}
+ : keyRepeatTimeout(500 * 1000000LL),
+ keyRepeatDelay(50 * 1000000LL),
+ keyRepeatEnabled(true) {}
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 653f595..463a952 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -227,10 +227,11 @@
virtual void cancelCurrentTouch() = 0;
/*
- * Updates key repeat configuration timeout and delay.
+ * Updates whether key repeat is enabled and key repeat configuration timeout and delay.
*/
virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
- std::chrono::nanoseconds delay) = 0;
+ std::chrono::nanoseconds delay,
+ bool keyRepeatEnabled) = 0;
/*
* Determine if a pointer from a device is being dispatched to the given window.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 65fb76d..b885ba1 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -43,9 +43,6 @@
InputDispatcherPolicyInterface() = default;
virtual ~InputDispatcherPolicyInterface() = default;
- /* Notifies the system that a configuration change has occurred. */
- virtual void notifyConfigurationChanged(nsecs_t when) = 0;
-
/* Notifies the system that an application does not have a focused window.
*/
virtual void notifyNoFocusedWindowAnr(
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 0b7f7c2..d8a9afa 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -38,7 +38,6 @@
virtual ~InputListenerInterface() { }
virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) = 0;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) = 0;
virtual void notifyKey(const NotifyKeyArgs& args) = 0;
virtual void notifyMotion(const NotifyMotionArgs& args) = 0;
virtual void notifySwitch(const NotifySwitchArgs& args) = 0;
@@ -60,7 +59,6 @@
explicit QueuedInputListener(InputListenerInterface& innerListener);
virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
virtual void notifyKey(const NotifyKeyArgs& args) override;
virtual void notifyMotion(const NotifyMotionArgs& args) override;
virtual void notifySwitch(const NotifySwitchArgs& args) override;
@@ -84,7 +82,6 @@
explicit TracedInputListener(const char* name, InputListenerInterface& innerListener);
virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
virtual void notifyKey(const NotifyKeyArgs& args) override;
virtual void notifyMotion(const NotifyMotionArgs& args) override;
virtual void notifySwitch(const NotifySwitchArgs& args) override;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 889ee09..2f6c6d7 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -34,7 +34,9 @@
#include <vector>
#include "PointerControllerInterface.h"
+#include "TouchpadHardwareState.h"
#include "VibrationElement.h"
+#include "include/gestures.h"
// Maximum supported size of a vibration pattern.
// Must be at least 2.
@@ -91,6 +93,9 @@
// The touchpad settings changed.
TOUCHPAD_SETTINGS = 1u << 13,
+ // The key remapping has changed.
+ KEY_REMAPPING = 1u << 14,
+
// All devices must be reopened.
MUST_REOPEN = 1u << 31,
};
@@ -227,6 +232,9 @@
// True to enable tap dragging on touchpads.
bool touchpadTapDraggingEnabled;
+ // True if hardware state update notifications should be sent to the policy.
+ bool shouldNotifyTouchpadHardwareState;
+
// True to enable a zone on the right-hand side of touchpads where clicks will be turned into
// context (a.k.a. "right") clicks.
bool touchpadRightClickZoneEnabled;
@@ -241,6 +249,9 @@
// True if a pointer icon should be shown for direct stylus pointers.
bool stylusPointerIconEnabled;
+ // Keycodes to be remapped.
+ std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
@@ -268,6 +279,7 @@
touchpadNaturalScrollingEnabled(true),
touchpadTapToClickEnabled(true),
touchpadTapDraggingEnabled(false),
+ shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false) {}
@@ -327,9 +339,6 @@
virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) = 0;
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0;
- virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const = 0;
-
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/* Toggle Caps Lock */
@@ -363,6 +372,8 @@
virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
+ virtual std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) = 0;
+
/* Return true if the device can send input events to the specified display. */
virtual bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) = 0;
@@ -397,6 +408,9 @@
* Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot.
*/
virtual DeviceId getLastUsedInputDeviceId() = 0;
+
+ /* Notifies that mouse cursor faded due to typing. */
+ virtual void notifyMouseCursorFadedOnTyping() = 0;
};
// --- TouchAffineTransformation ---
@@ -451,6 +465,13 @@
*/
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
+ /* Sends the hardware state of a connected touchpad */
+ virtual void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) = 0;
+
+ /* Sends the Info of gestures that happen on the touchpad. */
+ virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0;
+
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
index 5e75027..fcd913d 100644
--- a/services/inputflinger/include/InputThread.h
+++ b/services/inputflinger/include/InputThread.h
@@ -38,6 +38,7 @@
std::string mName;
std::function<void()> mThreadWake;
sp<Thread> mThread;
+ bool applyInputEventProfile();
};
} // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index db417cf..14487fe 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -39,21 +39,6 @@
NotifyInputDevicesChangedArgs& operator=(const NotifyInputDevicesChangedArgs&) = default;
};
-/* Describes a configuration change event. */
-struct NotifyConfigurationChangedArgs {
- int32_t id;
- nsecs_t eventTime;
-
- inline NotifyConfigurationChangedArgs() {}
-
- NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
-
- bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
-
- NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
- NotifyConfigurationChangedArgs& operator=(const NotifyConfigurationChangedArgs&) = default;
-};
-
/* Describes a key event. */
struct NotifyKeyArgs {
int32_t id;
@@ -234,8 +219,8 @@
};
using NotifyArgs =
- std::variant<NotifyInputDevicesChangedArgs, NotifyConfigurationChangedArgs, NotifyKeyArgs,
- NotifyMotionArgs, NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
+ std::variant<NotifyInputDevicesChangedArgs, NotifyKeyArgs, NotifyMotionArgs,
+ NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
const char* toString(const NotifyArgs& args);
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 7a85c12..e1f8fda 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -58,6 +58,9 @@
/* Returns true if any InputConnection is currently active. */
virtual bool isInputMethodConnectionActive() = 0;
+
+ /* Notifies that mouse cursor faded due to typing. */
+ virtual void notifyMouseCursorFadedOnTyping() = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index e34ed0f..8f3d9ca 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -72,10 +72,6 @@
/* Dumps the state of the pointer controller. */
virtual std::string dump() = 0;
- /* Gets the bounds of the region that the pointer can traverse.
- * Returns true if the bounds are available. */
- virtual std::optional<FloatRect> getBounds() const = 0;
-
/* Move the pointer. */
virtual void move(float deltaX, float deltaY) = 0;
diff --git a/libs/tracing_perfetto/include/trace_result.h b/services/inputflinger/include/TouchpadHardwareState.h
similarity index 60%
copy from libs/tracing_perfetto/include/trace_result.h
copy to services/inputflinger/include/TouchpadHardwareState.h
index f7581fc..a8dd44c 100644
--- a/libs/tracing_perfetto/include/trace_result.h
+++ b/services/inputflinger/include/TouchpadHardwareState.h
@@ -14,17 +14,19 @@
* limitations under the License.
*/
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
+#pragma once
-namespace tracing_perfetto {
+#include <include/gestures.h>
+#include <vector>
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
+namespace android {
+
+// A Gestures library HardwareState struct (from libchrome-gestures), but bundled
+// with a vector to contain its FingerStates, so you don't have to worry about where
+// that memory is allocated.
+struct SelfContainedHardwareState {
+ HardwareState state;
+ std::vector<FingerState> fingers;
};
-}
-
-#endif // TRACE_RESULT_H
+} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index e76b648..b76e8c5 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -78,6 +78,7 @@
name: "libinputreader_defaults",
srcs: [":libinputreader_sources"],
shared_libs: [
+ "android.companion.virtualdevice.flags-aconfig-cc",
"libbase",
"libcap",
"libcrypto",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index fe70a51..0865eed 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -33,6 +33,8 @@
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <android_companion_virtualdevice_flags.h>
+
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
@@ -68,6 +70,8 @@
namespace android {
+namespace vd_flags = android::companion::virtualdevice::flags;
+
using namespace ftl::flag_operators;
static const char* DEVICE_INPUT_PATH = "/dev/input";
@@ -513,10 +517,10 @@
// --- RawAbsoluteAxisInfo ---
-std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info) {
- if (info.valid) {
- out << "min=" << info.minValue << ", max=" << info.maxValue << ", flat=" << info.flat
- << ", fuzz=" << info.fuzz << ", resolution=" << info.resolution;
+std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info) {
+ if (info) {
+ out << "min=" << info->minValue << ", max=" << info->maxValue << ", flat=" << info->flat
+ << ", fuzz=" << info->fuzz << ", resolution=" << info->resolution;
} else {
out << "unknown range";
}
@@ -645,7 +649,6 @@
continue;
}
auto& [axisInfo, value] = absState[axis];
- axisInfo.valid = true;
axisInfo.minValue = info.minimum;
axisInfo.maxValue = info.maximum;
axisInfo.flat = info.flat;
@@ -885,7 +888,6 @@
: mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
- mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),
mNeedToScanDevices(true),
mPendingEventCount(0),
@@ -998,26 +1000,23 @@
return *device->configuration;
}
-status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
- outAxisInfo->clear();
+std::optional<RawAbsoluteAxisInfo> EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis) const {
if (axis < 0 || axis > ABS_MAX) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
std::scoped_lock _l(mLock);
const Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
// We can read the RawAbsoluteAxisInfo even if the device is disabled and doesn't have a valid
// fd, because the info is populated once when the device is first opened, and it doesn't change
// throughout the device lifecycle.
auto it = device->absState.find(axis);
if (it == device->absState.end()) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
- *outAxisInfo = it->second.info;
- return OK;
+ return it->second.info;
}
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
@@ -1130,22 +1129,20 @@
return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
-status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
- *outValue = 0;
+std::optional<int32_t> EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const {
if (axis < 0 || axis > ABS_MAX) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
std::scoped_lock _l(mLock);
const Device* device = getDeviceLocked(deviceId);
if (device == nullptr || !device->hasValidFd()) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
const auto it = device->absState.find(axis);
if (it == device->absState.end()) {
- return NAME_NOT_FOUND;
+ return std::nullopt;
}
- *outValue = it->second.value;
- return OK;
+ return it->second.value;
}
base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
@@ -1180,7 +1177,8 @@
return false;
}
-void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+void EventHub::setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
@@ -1188,7 +1186,7 @@
}
const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm) {
- kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+ kcm->setKeyRemapping(keyRemapping);
}
}
@@ -1879,7 +1877,6 @@
.type = DEVICE_REMOVED,
});
it = mClosingDevices.erase(it);
- mNeedToSendFinishedDeviceScan = true;
if (events.size() == EVENT_BUFFER_SIZE) {
break;
}
@@ -1888,7 +1885,6 @@
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
- mNeedToSendFinishedDeviceScan = true;
}
while (!mOpeningDevices.empty()) {
@@ -1917,18 +1913,6 @@
if (!inserted) {
ALOGW("Device id %d exists, replaced.", device->id);
}
- mNeedToSendFinishedDeviceScan = true;
- if (events.size() == EVENT_BUFFER_SIZE) {
- break;
- }
- }
-
- if (mNeedToSendFinishedDeviceScan) {
- mNeedToSendFinishedDeviceScan = false;
- events.push_back({
- .when = now,
- .type = FINISHED_DEVICE_SCAN,
- });
if (events.size() == EVENT_BUFFER_SIZE) {
break;
}
@@ -2503,6 +2487,12 @@
}
}
+ // See if the device is a rotary encoder with a single scroll axis and nothing else.
+ if (vd_flags::virtual_rotary() && device->classes == ftl::Flags<InputDeviceClass>(0) &&
+ device->relBitmask.test(REL_WHEEL) && !device->relBitmask.test(REL_HWHEEL)) {
+ device->classes |= InputDeviceClass::ROTARY_ENCODER;
+ }
+
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == ftl::Flags<InputDeviceClass>(0)) {
ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 2daf195..6185f1a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -365,6 +365,18 @@
// so update the enabled state when there is a change in display info.
out += updateEnableState(when, readerConfig, forceEnable);
}
+
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEY_REMAPPING)) {
+ const bool isFullKeyboard =
+ (mSources & AINPUT_SOURCE_KEYBOARD) == AINPUT_SOURCE_KEYBOARD &&
+ mKeyboardType == KeyboardType::ALPHABETIC;
+ if (isFullKeyboard) {
+ for_each_subdevice([&readerConfig](auto& context) {
+ context.setKeyRemapping(readerConfig.keyRemapping);
+ });
+ bumpGeneration();
+ }
+ }
}
return out;
}
@@ -689,12 +701,6 @@
});
}
-void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
- for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
- context.addKeyRemapping(fromKeyCode, toKeyCode);
- });
-}
-
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
@@ -725,6 +731,15 @@
return count;
}
+std::optional<HardwareProperties> InputDevice::getTouchpadHardwareProperties() {
+ std::optional<HardwareProperties> result = first_in_mappers<HardwareProperties>(
+ [](InputMapper& mapper) -> std::optional<HardwareProperties> {
+ return mapper.getTouchpadHardwareProperties();
+ });
+
+ return result;
+}
+
void InputDevice::updateLedState(bool reset) {
for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ab13ad4..e579390 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -33,6 +33,7 @@
#include <utils/Thread.h>
#include "InputDevice.h"
+#include "include/gestures.h"
using android::base::StringPrintf;
@@ -180,6 +181,9 @@
}
if (oldGeneration != mGeneration) {
+ // Reset global meta state because it depends on connected input devices.
+ updateGlobalMetaStateLocked();
+
inputDevicesChanged = true;
inputDevices = getInputDevicesLocked();
mPendingArgs.emplace_back(
@@ -247,9 +251,6 @@
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChangedLocked(rawEvent->when);
- break;
default:
ALOG_ASSERT(false); // can't happen
break;
@@ -414,14 +415,6 @@
return ++mNextInputDeviceId;
}
-void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
- // Reset global meta state because it depends on the list of all configured devices.
- updateGlobalMetaStateLocked();
-
- // Enqueue configuration changed.
- mPendingArgs.emplace_back(NotifyConfigurationChangedArgs{mContext.getNextId(), when});
-}
-
void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
@@ -632,15 +625,6 @@
return result;
}
-void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
- std::scoped_lock _l(mLock);
-
- InputDevice* device = findInputDeviceLocked(deviceId);
- if (device != nullptr) {
- device->addKeyRemapping(fromKeyCode, toKeyCode);
- }
-}
-
int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
std::scoped_lock _l(mLock);
@@ -825,6 +809,18 @@
return device->getDeviceInfo().getSensors();
}
+std::optional<HardwareProperties> InputReader::getTouchpadHardwareProperties(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+
+ if (device == nullptr) {
+ return {};
+ }
+
+ return device->getTouchpadHardwareProperties();
+}
+
bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) {
std::scoped_lock _l(mLock);
@@ -907,6 +903,12 @@
return mLastUsedDeviceId;
}
+void InputReader::notifyMouseCursorFadedOnTyping() {
+ std::scoped_lock _l(mLock);
+ // disable touchpad taps when cursor has faded due to typing
+ mPreventingTouchpadTaps = true;
+}
+
void InputReader::dump(std::string& dump) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 7cf584d..edc3037 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -21,6 +21,7 @@
#include <filesystem>
#include <functional>
#include <map>
+#include <optional>
#include <ostream>
#include <string>
#include <unordered_map>
@@ -70,18 +71,14 @@
/* Describes an absolute axis. */
struct RawAbsoluteAxisInfo {
- bool valid{false}; // true if the information is valid, false otherwise
-
int32_t minValue{}; // minimum value
int32_t maxValue{}; // maximum value
int32_t flat{}; // center flat position, eg. flat == 8 means center is between -8 and 8
int32_t fuzz{}; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
int32_t resolution{}; // resolution in units per mm or radians per mm
-
- inline void clear() { *this = RawAbsoluteAxisInfo(); }
};
-std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info);
+std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info);
/*
* Input device classes.
@@ -257,9 +254,6 @@
DEVICE_ADDED = 0x10000000,
// Sent when a device is removed.
DEVICE_REMOVED = 0x20000000,
- // Sent when all added/removed devices from the most recent scan have been reported.
- // This event is always sent at least once.
- FINISHED_DEVICE_SCAN = 0x30000000,
FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
@@ -278,8 +272,8 @@
*/
virtual std::optional<PropertyMap> getConfiguration(int32_t deviceId) const = 0;
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const = 0;
+ virtual std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const = 0;
virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
@@ -287,8 +281,8 @@
virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
- virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const = 0;
+ virtual void setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const = 0;
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
@@ -339,8 +333,7 @@
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const = 0;
+ virtual std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const = 0;
/* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size
* (slotCount + 1). The value at the 0 index is set to queried axis. */
virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
@@ -511,8 +504,8 @@
std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override final;
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override final;
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const override final;
bool hasRelativeAxis(int32_t deviceId, int axis) const override final;
@@ -520,8 +513,8 @@
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const override final;
+ void setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const override final;
status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState,
@@ -559,8 +552,8 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final;
int32_t getKeyCodeForKeyLocation(int32_t deviceId,
int32_t locationKeyCode) const override final;
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override final;
+ std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId,
+ int32_t axis) const override final;
base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
size_t slotCount) const override final;
@@ -793,7 +786,6 @@
std::vector<std::unique_ptr<Device>> mOpeningDevices;
std::vector<std::unique_ptr<Device>> mClosingDevices;
- bool mNeedToSendFinishedDeviceScan;
bool mNeedToReopenDevices;
bool mNeedToScanDevices;
std::vector<std::string> mExcludedDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4374ff5..62cc4da 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -72,7 +72,7 @@
inline std::optional<std::string> getDeviceTypeAssociation() const {
return mAssociatedDeviceType;
}
- inline std::optional<DisplayViewport> getAssociatedViewport() const {
+ inline virtual std::optional<DisplayViewport> getAssociatedViewport() const {
return mAssociatedViewport;
}
inline bool hasMic() const { return mHasMic; }
@@ -124,8 +124,6 @@
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
- void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
-
void setKeyboardType(KeyboardType keyboardType);
void bumpGeneration();
@@ -141,6 +139,8 @@
size_t getMapperCount();
+ std::optional<HardwareProperties> getTouchpadHardwareProperties();
+
// construct and add a mapper to the input device
template <class T, typename... Args>
T& addMapper(int32_t eventHubId, Args... args) {
@@ -306,19 +306,17 @@
inline int32_t getDeviceControllerNumber() const {
return mEventHub->getDeviceControllerNumber(mId);
}
- inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
- if (const auto status = mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); status != OK) {
- return status;
- }
+ inline std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t code) const {
+ std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
// Validate axis info for InputDevice.
- if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
+ if (info && info->minValue == info->maxValue) {
// Historically, we deem axes with the same min and max values as invalid to avoid
// dividing by zero when scaling by max - min.
// TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used.
- axisInfo->valid = false;
+ return std::nullopt;
}
- return OK;
+ return info;
}
inline bool hasRelativeAxis(int32_t code) const {
return mEventHub->hasRelativeAxis(mId, code);
@@ -329,8 +327,8 @@
inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
- inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
- mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+ inline void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) const {
+ mEventHub->setKeyRemapping(mId, keyRemapping);
}
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
@@ -380,8 +378,8 @@
return mEventHub->getKeyCodeForKeyLocation(mId, locationKeyCode);
}
inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); }
- inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
- return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
+ inline std::optional<int32_t> getAbsoluteAxisValue(int32_t code) const {
+ return mEventHub->getAbsoluteAxisValue(mId, code);
}
inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis,
size_t slotCount) const {
@@ -433,9 +431,7 @@
}
inline bool hasAbsoluteAxis(int32_t code) const {
- RawAbsoluteAxisInfo info;
- mEventHub->getAbsoluteAxisInfo(mId, code, &info);
- return info.valid;
+ return mEventHub->getAbsoluteAxisInfo(mId, code).has_value();
}
inline bool isKeyPressed(int32_t scanCode) const {
return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
@@ -443,11 +439,6 @@
inline bool isKeyCodePressed(int32_t keyCode) const {
return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN;
}
- inline int32_t getAbsoluteAxisValue(int32_t code) const {
- int32_t value;
- mEventHub->getAbsoluteAxisValue(mId, code, &value);
- return value;
- }
inline bool isDeviceEnabled() { return mEventHub->isDeviceEnabled(mId); }
inline status_t enableDevice() { return mEventHub->enableDevice(mId); }
inline status_t disableDevice() { return mEventHub->disableDevice(mId); }
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 6f8c289..1003871 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -65,8 +65,6 @@
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
-
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
void toggleCapsLockState(int32_t deviceId) override;
@@ -104,6 +102,8 @@
std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override;
+ std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) override;
+
bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override;
bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) override;
@@ -118,6 +118,8 @@
DeviceId getLastUsedInputDeviceId() override;
+ void notifyMouseCursorFadedOnTyping() override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
@@ -199,7 +201,7 @@
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
- // true if tap-to-click on touchpad currently disabled
+ // true if tap-to-click on touchpad is currently disabled
bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
// records timestamp of the last key press on the physical keyboard
@@ -219,8 +221,6 @@
size_t count) REQUIRES(mLock);
[[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
- void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock);
-
int32_t mGlobalMetaState GUARDED_BY(mLock);
void updateGlobalMetaStateLocked() REQUIRES(mLock);
int32_t getGlobalMetaStateLocked() REQUIRES(mLock);
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index 90685de..dd46bbc 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -16,17 +16,23 @@
#include "CapturedTouchpadEventConverter.h"
+#include <optional>
#include <sstream>
#include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
#include <input/PrintTools.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
+namespace input_flags = com::android::input::flags;
+
namespace android {
namespace {
+static constexpr uint32_t SOURCE = AINPUT_SOURCE_TOUCHPAD;
+
int32_t actionWithIndex(int32_t action, int32_t index) {
return action | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
}
@@ -42,6 +48,12 @@
return i;
}
+void addRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis,
+ RawAbsoluteAxisInfo& evdevAxis) {
+ deviceInfo.addMotionRange(androidAxis, SOURCE, evdevAxis.minValue, evdevAxis.maxValue,
+ evdevAxis.flat, evdevAxis.fuzz, evdevAxis.resolution);
+}
+
} // namespace
CapturedTouchpadEventConverter::CapturedTouchpadEventConverter(
@@ -53,32 +65,33 @@
mMotionAccumulator(motionAccumulator),
mHasTouchMinor(deviceContext.hasAbsoluteAxis(ABS_MT_TOUCH_MINOR)),
mHasToolMinor(deviceContext.hasAbsoluteAxis(ABS_MT_WIDTH_MINOR)) {
- RawAbsoluteAxisInfo orientationInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
- if (orientationInfo.valid) {
- if (orientationInfo.maxValue > 0) {
- mOrientationScale = M_PI_2 / orientationInfo.maxValue;
- } else if (orientationInfo.minValue < 0) {
- mOrientationScale = -M_PI_2 / orientationInfo.minValue;
+ if (std::optional<RawAbsoluteAxisInfo> orientation =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ orientation) {
+ if (orientation->maxValue > 0) {
+ mOrientationScale = M_PI_2 / orientation->maxValue;
+ } else if (orientation->minValue < 0) {
+ mOrientationScale = -M_PI_2 / orientation->minValue;
}
}
// TODO(b/275369880): support touch.pressure.calibration and .scale properties when captured.
- RawAbsoluteAxisInfo pressureInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
- if (pressureInfo.valid && pressureInfo.maxValue > 0) {
- mPressureScale = 1.0 / pressureInfo.maxValue;
+ if (std::optional<RawAbsoluteAxisInfo> pressure =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE);
+ pressure && pressure->maxValue > 0) {
+ mPressureScale = 1.0 / pressure->maxValue;
}
- RawAbsoluteAxisInfo touchMajorInfo, toolMajorInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &touchMajorInfo);
- deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &toolMajorInfo);
- mHasTouchMajor = touchMajorInfo.valid;
- mHasToolMajor = toolMajorInfo.valid;
- if (mHasTouchMajor && touchMajorInfo.maxValue != 0) {
- mSizeScale = 1.0f / touchMajorInfo.maxValue;
- } else if (mHasToolMajor && toolMajorInfo.maxValue != 0) {
- mSizeScale = 1.0f / toolMajorInfo.maxValue;
+ std::optional<RawAbsoluteAxisInfo> touchMajor =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
+ std::optional<RawAbsoluteAxisInfo> toolMajor =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
+ mHasTouchMajor = touchMajor.has_value();
+ mHasToolMajor = toolMajor.has_value();
+ if (mHasTouchMajor && touchMajor->maxValue != 0) {
+ mSizeScale = 1.0f / touchMajor->maxValue;
+ } else if (mHasToolMajor && toolMajor->maxValue != 0) {
+ mSizeScale = 1.0f / toolMajor->maxValue;
}
}
@@ -106,22 +119,27 @@
}
void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const {
- tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
- tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
+ if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
+ tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
+ AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
+ tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
+ AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
+ } else {
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
+ }
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MINOR, ABS_MT_WIDTH_MINOR);
- RawAbsoluteAxisInfo pressureInfo;
- mDeviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
- if (pressureInfo.valid) {
+ if (mDeviceContext.hasAbsoluteAxis(ABS_MT_PRESSURE)) {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0, 1, 0, 0, 0);
}
- RawAbsoluteAxisInfo orientationInfo;
- mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
- if (orientationInfo.valid && (orientationInfo.maxValue > 0 || orientationInfo.minValue < 0)) {
+ if (std::optional<RawAbsoluteAxisInfo> orientation =
+ mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ orientation && (orientation->maxValue > 0 || orientation->minValue < 0)) {
info.addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, SOURCE, -M_PI_2, M_PI_2, 0, 0, 0);
}
@@ -133,11 +151,25 @@
void CapturedTouchpadEventConverter::tryAddRawMotionRange(InputDeviceInfo& deviceInfo,
int32_t androidAxis,
int32_t evdevAxis) const {
- RawAbsoluteAxisInfo info;
- mDeviceContext.getAbsoluteAxisInfo(evdevAxis, &info);
- if (info.valid) {
- deviceInfo.addMotionRange(androidAxis, SOURCE, info.minValue, info.maxValue, info.flat,
- info.fuzz, info.resolution);
+ std::optional<RawAbsoluteAxisInfo> info = mDeviceContext.getAbsoluteAxisInfo(evdevAxis);
+ if (info) {
+ addRawMotionRange(/*byref*/ deviceInfo, androidAxis, *info);
+ }
+}
+
+void CapturedTouchpadEventConverter::tryAddRawMotionRangeWithRelative(InputDeviceInfo& deviceInfo,
+ int32_t androidAxis,
+ int32_t androidRelativeAxis,
+ int32_t evdevAxis) const {
+ std::optional<RawAbsoluteAxisInfo> axisInfo = mDeviceContext.getAbsoluteAxisInfo(evdevAxis);
+ if (axisInfo) {
+ addRawMotionRange(/*byref*/ deviceInfo, androidAxis, *axisInfo);
+
+ // The largest movement we could possibly report on a relative axis is from the minimum to
+ // the maximum (or vice versa) of the absolute axis.
+ float range = axisInfo->maxValue - axisInfo->minValue;
+ deviceInfo.addMotionRange(androidRelativeAxis, SOURCE, -range, range, axisInfo->flat,
+ axisInfo->fuzz, axisInfo->resolution);
}
}
@@ -164,7 +196,7 @@
std::list<NotifyArgs> out;
std::vector<PointerCoords> coords;
std::vector<PointerProperties> properties;
- std::map<size_t, size_t> coordsIndexForSlotNumber;
+ std::map<size_t /*slotNumber*/, size_t /*coordsIndex*/> coordsIndexForSlotNumber;
// For all the touches that were already down, send a MOVE event with their updated coordinates.
// A convention of the MotionEvent API is that pointer coordinates in UP events match the
@@ -176,11 +208,19 @@
// to stay perfectly still between frames, and if it does the worst that can happen is
// an extra MOVE event, so it's not worth the overhead of checking for changes.
coordsIndexForSlotNumber[slotNumber] = coords.size();
- coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber)));
+ coords.push_back(makePointerCoordsForSlot(slotNumber));
properties.push_back({.id = pointerId, .toolType = ToolType::FINGER});
}
out.push_back(
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties));
+ if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
+ // For any further events we send from this sync, the pointers won't have moved relative
+ // to the positions we just reported in this MOVE event, so zero out the relative axes.
+ for (PointerCoords& pointer : coords) {
+ pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+ }
+ }
}
std::vector<size_t> upSlots, downSlots;
@@ -235,6 +275,9 @@
/*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0));
freePointerIdForSlot(slotNumber);
+ if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
+ mPreviousCoordsForSlotNumber.erase(slotNumber);
+ }
coords.erase(coords.begin() + indexToRemove);
properties.erase(properties.begin() + indexToRemove);
// Now that we've removed some coords and properties, we might have to update the slot
@@ -255,7 +298,7 @@
: actionWithIndex(AMOTION_EVENT_ACTION_POINTER_DOWN, coordsIndex);
coordsIndexForSlotNumber[slotNumber] = coordsIndex;
- coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber)));
+ coords.push_back(makePointerCoordsForSlot(slotNumber));
properties.push_back(
{.id = allocatePointerIdToSlot(slotNumber), .toolType = ToolType::FINGER});
@@ -287,12 +330,22 @@
AMOTION_EVENT_INVALID_CURSOR_POSITION, mDownTime, /*videoFrames=*/{});
}
-PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot(
- const MultiTouchMotionAccumulator::Slot& slot) const {
+PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot(size_t slotNumber) {
+ const MultiTouchMotionAccumulator::Slot& slot = mMotionAccumulator.getSlot(slotNumber);
PointerCoords coords;
coords.clear();
coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX());
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY());
+ if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
+ if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
+ it != mPreviousCoordsForSlotNumber.end()) {
+ auto [oldX, oldY] = it->second;
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
+ }
+ mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
+ }
+
coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor());
coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor());
coords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, slot.getToolMajor());
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
index 9b6df7a..d6c0708 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
@@ -21,6 +21,7 @@
#include <map>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include <android/input.h>
@@ -49,12 +50,14 @@
private:
void tryAddRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis,
int32_t evdevAxis) const;
+ void tryAddRawMotionRangeWithRelative(InputDeviceInfo& deviceInfo, int32_t androidAxis,
+ int32_t androidRelativeAxis, int32_t evdevAxis) const;
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
[[nodiscard]] NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
const std::vector<PointerCoords>& coords,
const std::vector<PointerProperties>& properties,
int32_t actionButton = 0, int32_t flags = 0);
- PointerCoords makePointerCoordsForSlot(const MultiTouchMotionAccumulator::Slot& slot) const;
+ PointerCoords makePointerCoordsForSlot(size_t slotNumber);
int32_t allocatePointerIdToSlot(size_t slotNumber);
void freePointerIdForSlot(size_t slotNumber);
@@ -76,8 +79,7 @@
std::bitset<MAX_POINTER_ID + 1> mPointerIdsInUse;
std::map<size_t, int32_t> mPointerIdForSlotNumber;
-
- static constexpr uint32_t SOURCE = AINPUT_SOURCE_TOUCHPAD;
+ std::map<size_t, std::pair<float, float>> mPreviousCoordsForSlotNumber;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 2108488..3fc370c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -25,9 +25,6 @@
namespace android {
-class CursorButtonAccumulator;
-class CursorScrollAccumulator;
-
/* Keeps track of cursor movements. */
class CursorMotionAccumulator {
public:
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 3af1d04..4cd37d7 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -33,7 +33,7 @@
void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- if (mRawPressureAxis.valid) {
+ if (mRawPressureAxis || mTouchButtonAccumulator.hasButtonTouch()) {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f);
}
@@ -50,7 +50,7 @@
std::list<NotifyArgs> ExternalStylusInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
ConfigurationChanges changes) {
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
+ mRawPressureAxis = getAbsoluteAxisInfo(ABS_PRESSURE);
mTouchButtonAccumulator.configure();
return {};
}
@@ -82,10 +82,10 @@
mStylusState.toolType = ToolType::STYLUS;
}
- if (mRawPressureAxis.valid) {
+ if (mRawPressureAxis) {
auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
- mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
- static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
+ mStylusState.pressure = (rawPressure - mRawPressureAxis->minValue) /
+ static_cast<float>(mRawPressureAxis->maxValue - mRawPressureAxis->minValue);
} else if (mTouchButtonAccumulator.hasButtonTouch()) {
mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
}
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index c040a7b..d48fd9b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <optional>
+
#include "InputMapper.h"
#include "SingleTouchMotionAccumulator.h"
@@ -43,7 +45,7 @@
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
- RawAbsoluteAxisInfo mRawPressureAxis;
+ std::optional<RawAbsoluteAxisInfo> mRawPressureAxis;
TouchButtonAccumulator mTouchButtonAccumulator;
StylusState mStylusState;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index b6c5c98..627df7f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -18,6 +18,7 @@
#include "InputMapper.h"
+#include <optional>
#include <sstream>
#include <ftl/enum.h>
@@ -116,15 +117,16 @@
return {};
}
-status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
- return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
+std::optional<RawAbsoluteAxisInfo> InputMapper::getAbsoluteAxisInfo(int32_t axis) {
+ return getDeviceContext().getAbsoluteAxisInfo(axis);
}
void InputMapper::bumpGeneration() {
getDeviceContext().bumpGeneration();
}
-void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump,
+ const std::optional<RawAbsoluteAxisInfo>& axis,
const char* name) {
std::stringstream out;
out << INDENT4 << name << ": " << axis << "\n";
@@ -138,4 +140,7 @@
dump += StringPrintf(INDENT4 "Tool Type: %s\n", ftl::enum_string(state.toolType).c_str());
}
+std::optional<HardwareProperties> InputMapper::getTouchpadHardwareProperties() {
+ return std::nullopt;
+}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 2c51448..75cc4bb 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <optional>
+
#include "EventHub.h"
#include "InputDevice.h"
#include "InputListener.h"
@@ -120,16 +122,19 @@
virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; }
virtual void updateLedState(bool reset) {}
+ virtual std::optional<HardwareProperties> getTouchpadHardwareProperties();
+
protected:
InputDeviceContext& mDeviceContext;
explicit InputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
- status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t axis);
void bumpGeneration();
- static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+ static void dumpRawAbsoluteAxisInfo(std::string& dump,
+ const std::optional<RawAbsoluteAxisInfo>& axis,
const char* name);
static void dumpStylusState(std::string& dump, const StylusState& state);
};
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 41e018d..3091714 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -117,9 +117,8 @@
continue; // axis must be claimed by a different device
}
- RawAbsoluteAxisInfo rawAxisInfo;
- getAbsoluteAxisInfo(abs, &rawAxisInfo);
- if (rawAxisInfo.valid) {
+ if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs);
+ rawAxisInfo) {
// Map axis.
AxisInfo axisInfo;
const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
@@ -129,7 +128,7 @@
axisInfo.mode = AxisInfo::MODE_NORMAL;
axisInfo.axis = -1;
}
- mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
+ mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo.value(), explicitlyMapped)});
}
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8eb6730..38dcd65 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -493,12 +493,6 @@
void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) {
InputReaderContext& context = *getContext();
context.setLastKeyDownTimestamp(downTime);
- // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
- // shortcuts
- bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode);
- if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
- context.setPreventingTouchpadTaps(true);
- }
}
uint32_t KeyboardInputMapper::getEventSource() const {
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 1986fe2..fd8224a 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -133,7 +133,7 @@
bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
(mTouchButtonAccumulator.isHovering() ||
- (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
+ (mRawPointerAxes.pressure && inSlot.getPressure() <= 0));
outPointer.isHovering = isHovering;
// Assign pointer id using tracking id if available.
@@ -189,21 +189,27 @@
void MultiTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
- getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
- getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
- getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
- getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
+ // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y
+ // axes, even though those axes are required to be supported.
+ if (const auto xInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_X); xInfo.has_value()) {
+ mRawPointerAxes.x = *xInfo;
+ }
+ if (const auto yInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_Y); yInfo.has_value()) {
+ mRawPointerAxes.y = *yInfo;
+ }
+ mRawPointerAxes.touchMajor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
+ mRawPointerAxes.touchMinor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR);
+ mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
+ mRawPointerAxes.toolMinor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR);
+ mRawPointerAxes.orientation = getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_MT_PRESSURE);
+ mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_MT_DISTANCE);
+ mRawPointerAxes.trackingId = getAbsoluteAxisInfo(ABS_MT_TRACKING_ID);
+ mRawPointerAxes.slot = getAbsoluteAxisInfo(ABS_MT_SLOT);
- if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
- mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
- size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
+ if (mRawPointerAxes.trackingId && mRawPointerAxes.slot && mRawPointerAxes.slot->minValue == 0 &&
+ mRawPointerAxes.slot->maxValue > 0) {
+ size_t slotCount = mRawPointerAxes.slot->maxValue + 1;
if (slotCount > MAX_SLOTS) {
ALOGW("MultiTouch Device %s reported %zu slots but the framework "
"only supports a maximum of %zu slots at this time.",
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 27ff52f..b72cc6e 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -27,11 +27,14 @@
namespace android {
+constexpr float kDefaultScaleFactor = 1.0f;
+
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
- : InputMapper(deviceContext, readerConfig), mOrientation(ui::ROTATION_0) {
- mSource = AINPUT_SOURCE_ROTARY_ENCODER;
-}
+ : InputMapper(deviceContext, readerConfig),
+ mSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ mScalingFactor(kDefaultScaleFactor),
+ mOrientation(ui::ROTATION_0) {}
RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
@@ -51,9 +54,10 @@
std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
if (!scalingFactor.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
- "default to 1.0!\n");
+ "default to %f!\n",
+ kDefaultScaleFactor);
}
- mScalingFactor = scalingFactor.value_or(1.0f);
+ mScalingFactor = scalingFactor.value_or(kDefaultScaleFactor);
info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
res.value_or(0.0f) * mScalingFactor);
}
@@ -84,12 +88,18 @@
}
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
- std::optional<DisplayViewport> internalViewport =
- config.getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = internalViewport->orientation;
+ if (getDeviceContext().getAssociatedViewport()) {
+ mDisplayId = getDeviceContext().getAssociatedViewport()->displayId;
+ mOrientation = getDeviceContext().getAssociatedViewport()->orientation;
} else {
- mOrientation = ui::ROTATION_0;
+ mDisplayId = ui::LogicalDisplayId::INVALID;
+ std::optional<DisplayViewport> internalViewport =
+ config.getDisplayViewportByType(ViewportType::INTERNAL);
+ if (internalViewport) {
+ mOrientation = internalViewport->orientation;
+ } else {
+ mOrientation = ui::ROTATION_0;
+ }
}
}
return out;
@@ -124,8 +134,6 @@
// Send motion event.
if (scrolled) {
int32_t metaState = getContext()->getGlobalMetaState();
- // This is not a pointer, so it's not associated with a display.
- ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
if (mOrientation == ui::ROTATION_180) {
scroll = -scroll;
@@ -147,7 +155,7 @@
out.push_back(
NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+ mDisplayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
metaState, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 14c540b..7e80415 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -47,6 +47,7 @@
int32_t mSource;
float mScalingFactor;
ui::Rotation mOrientation;
+ ui::LogicalDisplayId mDisplayId = ui::LogicalDisplayId::INVALID;
std::unique_ptr<SlopController> mSlopController;
explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index d7f2993..4233f78 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -133,9 +133,8 @@
.test(InputDeviceClass::SENSOR))) {
continue;
}
- RawAbsoluteAxisInfo rawAxisInfo;
- getAbsoluteAxisInfo(abs, &rawAxisInfo);
- if (rawAxisInfo.valid) {
+ if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs);
+ rawAxisInfo) {
AxisInfo axisInfo;
// Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion
// input events
@@ -146,7 +145,7 @@
if (ret.ok()) {
InputDeviceSensorType sensorType = (*ret).first;
int32_t sensorDataIndex = (*ret).second;
- const Axis& axis = createAxis(axisInfo, rawAxisInfo);
+ const Axis& axis = createAxis(axisInfo, rawAxisInfo.value());
parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis);
mAxes.insert({abs, axis});
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 140bb0c..cef1837 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -44,7 +44,7 @@
bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
(mTouchButtonAccumulator.isHovering() ||
- (mRawPointerAxes.pressure.valid &&
+ (mRawPointerAxes.pressure &&
mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
outState->rawPointerData.markIdBit(0, isHovering);
@@ -72,13 +72,19 @@
void SingleTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
- getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
- getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
+ // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y
+ // axes, even though those axes are required to be supported.
+ if (const auto xInfo = getAbsoluteAxisInfo(ABS_X); xInfo.has_value()) {
+ mRawPointerAxes.x = *xInfo;
+ }
+ if (const auto yInfo = getAbsoluteAxisInfo(ABS_Y); yInfo.has_value()) {
+ mRawPointerAxes.y = *yInfo;
+ }
+ mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_PRESSURE);
+ mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_TOOL_WIDTH);
+ mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_DISTANCE);
+ mRawPointerAxes.tiltX = getAbsoluteAxisInfo(ABS_TILT_X);
+ mRawPointerAxes.tiltY = getAbsoluteAxisInfo(ABS_TILT_Y);
}
bool SingleTouchInputMapper::hasStylus() const {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 64df226..5c90cbb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -149,7 +149,10 @@
// The SOURCE_BLUETOOTH_STYLUS is added to events dynamically if the current stream is modified
// by the external stylus state. That's why we don't add it directly to mSource during
// configuration.
- return mSource | (hasExternalStylus() ? AINPUT_SOURCE_BLUETOOTH_STYLUS : 0);
+ return mSource |
+ (mExternalStylusPresence == ExternalStylusPresence::TOUCH_FUSION
+ ? AINPUT_SOURCE_BLUETOOTH_STYLUS
+ : 0);
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
@@ -270,8 +273,8 @@
}
dump += INDENT3 "Stylus Fusion:\n";
- dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
- toString(mExternalStylusConnected));
+ dump += StringPrintf(INDENT4 "ExternalStylusPresence: %s\n",
+ ftl::enum_string(mExternalStylusPresence).c_str());
dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n",
toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
@@ -356,11 +359,19 @@
void TouchInputMapper::resolveExternalStylusPresence() {
std::vector<InputDeviceInfo> devices;
getContext()->getExternalStylusDevices(devices);
- mExternalStylusConnected = !devices.empty();
-
- if (!mExternalStylusConnected) {
+ if (devices.empty()) {
+ mExternalStylusPresence = ExternalStylusPresence::NONE;
resetExternalStylus();
+ return;
}
+ mExternalStylusPresence =
+ std::any_of(devices.begin(), devices.end(),
+ [](const auto& info) {
+ return info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
+ AINPUT_SOURCE_STYLUS) != nullptr;
+ })
+ ? ExternalStylusPresence::TOUCH_FUSION
+ : ExternalStylusPresence::BUTTON_FUSION;
}
TouchInputMapper::Parameters TouchInputMapper::computeParameters(
@@ -520,7 +531,7 @@
}
bool TouchInputMapper::hasExternalStylus() const {
- return mExternalStylusConnected;
+ return mExternalStylusPresence != ExternalStylusPresence::NONE;
}
/**
@@ -600,10 +611,10 @@
const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height);
// Size factors.
- if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
- } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
+ if (mRawPointerAxes.touchMajor && mRawPointerAxes.touchMajor->maxValue != 0) {
+ mSizeScale = 1.0f / mRawPointerAxes.touchMajor->maxValue;
+ } else if (mRawPointerAxes.toolMajor && mRawPointerAxes.toolMajor->maxValue != 0) {
+ mSizeScale = 1.0f / mRawPointerAxes.toolMajor->maxValue;
} else {
mSizeScale = 0.0f;
}
@@ -618,18 +629,18 @@
.resolution = 0,
};
- if (mRawPointerAxes.touchMajor.valid) {
- mRawPointerAxes.touchMajor.resolution =
- clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution);
- mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor.resolution;
+ if (mRawPointerAxes.touchMajor) {
+ mRawPointerAxes.touchMajor->resolution =
+ clampResolution("touchMajor", mRawPointerAxes.touchMajor->resolution);
+ mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor->resolution;
}
mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
mOrientedRanges.touchMinor->axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
- if (mRawPointerAxes.touchMinor.valid) {
- mRawPointerAxes.touchMinor.resolution =
- clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution);
- mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor.resolution;
+ if (mRawPointerAxes.touchMinor) {
+ mRawPointerAxes.touchMinor->resolution =
+ clampResolution("touchMinor", mRawPointerAxes.touchMinor->resolution);
+ mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor->resolution;
}
mOrientedRanges.toolMajor = InputDeviceInfo::MotionRange{
@@ -641,18 +652,18 @@
.fuzz = 0,
.resolution = 0,
};
- if (mRawPointerAxes.toolMajor.valid) {
- mRawPointerAxes.toolMajor.resolution =
- clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution);
- mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor.resolution;
+ if (mRawPointerAxes.toolMajor) {
+ mRawPointerAxes.toolMajor->resolution =
+ clampResolution("toolMajor", mRawPointerAxes.toolMajor->resolution);
+ mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor->resolution;
}
mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
mOrientedRanges.toolMinor->axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
- if (mRawPointerAxes.toolMinor.valid) {
- mRawPointerAxes.toolMinor.resolution =
- clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution);
- mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor.resolution;
+ if (mRawPointerAxes.toolMinor) {
+ mRawPointerAxes.toolMinor->resolution =
+ clampResolution("toolMinor", mRawPointerAxes.toolMinor->resolution);
+ mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor->resolution;
}
if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
@@ -704,9 +715,10 @@
mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
if (mCalibration.pressureScale) {
mPressureScale = *mCalibration.pressureScale;
- pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
- } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
- mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
+ pressureMax = mPressureScale *
+ (mRawPointerAxes.pressure ? mRawPointerAxes.pressure->maxValue : 0);
+ } else if (mRawPointerAxes.pressure && mRawPointerAxes.pressure->maxValue != 0) {
+ mPressureScale = 1.0f / mRawPointerAxes.pressure->maxValue;
}
}
@@ -725,18 +737,18 @@
mTiltXScale = 0;
mTiltYCenter = 0;
mTiltYScale = 0;
- mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
+ mHaveTilt = mRawPointerAxes.tiltX && mRawPointerAxes.tiltY;
if (mHaveTilt) {
- mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
- mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
+ mTiltXCenter = avg(mRawPointerAxes.tiltX->minValue, mRawPointerAxes.tiltX->maxValue);
+ mTiltYCenter = avg(mRawPointerAxes.tiltY->minValue, mRawPointerAxes.tiltY->maxValue);
mTiltXScale = M_PI / 180;
mTiltYScale = M_PI / 180;
- if (mRawPointerAxes.tiltX.resolution) {
- mTiltXScale = 1.0 / mRawPointerAxes.tiltX.resolution;
+ if (mRawPointerAxes.tiltX->resolution) {
+ mTiltXScale = 1.0 / mRawPointerAxes.tiltX->resolution;
}
- if (mRawPointerAxes.tiltY.resolution) {
- mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution;
+ if (mRawPointerAxes.tiltY->resolution) {
+ mTiltYScale = 1.0 / mRawPointerAxes.tiltY->resolution;
}
mOrientedRanges.tilt = InputDeviceInfo::MotionRange{
@@ -766,11 +778,11 @@
} else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) {
if (mCalibration.orientationCalibration ==
Calibration::OrientationCalibration::INTERPOLATED) {
- if (mRawPointerAxes.orientation.valid) {
- if (mRawPointerAxes.orientation.maxValue > 0) {
- mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
- } else if (mRawPointerAxes.orientation.minValue < 0) {
- mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
+ if (mRawPointerAxes.orientation) {
+ if (mRawPointerAxes.orientation->maxValue > 0) {
+ mOrientationScale = M_PI_2 / mRawPointerAxes.orientation->maxValue;
+ } else if (mRawPointerAxes.orientation->minValue < 0) {
+ mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation->minValue;
} else {
mOrientationScale = 0;
}
@@ -795,14 +807,14 @@
mDistanceScale = mCalibration.distanceScale.value_or(1.0f);
}
+ const bool hasDistance = mRawPointerAxes.distance.has_value();
mOrientedRanges.distance = InputDeviceInfo::MotionRange{
-
.axis = AMOTION_EVENT_AXIS_DISTANCE,
.source = mSource,
- .min = mRawPointerAxes.distance.minValue * mDistanceScale,
- .max = mRawPointerAxes.distance.maxValue * mDistanceScale,
+ .min = hasDistance ? mRawPointerAxes.distance->minValue * mDistanceScale : 0,
+ .max = hasDistance ? mRawPointerAxes.distance->maxValue * mDistanceScale : 0,
.flat = 0,
- .fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale,
+ .fuzz = hasDistance ? mRawPointerAxes.distance->fuzz * mDistanceScale : 0,
.resolution = 0,
};
}
@@ -931,7 +943,7 @@
mSource |= AINPUT_SOURCE_STYLUS;
}
} else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
- mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
+ mSource = AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD;
mDeviceMode = DeviceMode::NAVIGATION;
} else {
ALOGW("Touch device '%s' has invalid parameters or configuration. The device will be "
@@ -943,12 +955,7 @@
const std::optional<DisplayViewport> newViewportOpt = findViewport();
// Ensure the device is valid and can be used.
- if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
- ALOGW("Touch device '%s' did not report support for X or Y axis! "
- "The device will be inoperable.",
- getDeviceName().c_str());
- mDeviceMode = DeviceMode::DISABLED;
- } else if (!newViewportOpt) {
+ if (!newViewportOpt) {
ALOGI("Touch device '%s' could not query the properties of its associated "
"display. The device will be inoperable until the display size "
"becomes available.",
@@ -1237,7 +1244,7 @@
void TouchInputMapper::resolveCalibration() {
// Size
- if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
+ if (mRawPointerAxes.touchMajor || mRawPointerAxes.toolMajor) {
if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) {
mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
}
@@ -1246,7 +1253,7 @@
}
// Pressure
- if (mRawPointerAxes.pressure.valid) {
+ if (mRawPointerAxes.pressure) {
if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) {
mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
}
@@ -1255,7 +1262,7 @@
}
// Orientation
- if (mRawPointerAxes.orientation.valid) {
+ if (mRawPointerAxes.orientation) {
if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) {
mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
}
@@ -1264,7 +1271,7 @@
}
// Distance
- if (mRawPointerAxes.distance.valid) {
+ if (mRawPointerAxes.distance) {
if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) {
mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED;
}
@@ -2251,25 +2258,25 @@
case Calibration::SizeCalibration::DIAMETER:
case Calibration::SizeCalibration::BOX:
case Calibration::SizeCalibration::AREA:
- if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
+ if (mRawPointerAxes.touchMajor && mRawPointerAxes.toolMajor) {
touchMajor = in.touchMajor;
- touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+ touchMinor = mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor;
toolMajor = in.toolMajor;
- toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
- : in.touchMajor;
- } else if (mRawPointerAxes.touchMajor.valid) {
+ toolMinor = mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor;
+ size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor)
+ : in.touchMajor;
+ } else if (mRawPointerAxes.touchMajor) {
toolMajor = touchMajor = in.touchMajor;
toolMinor = touchMinor =
- mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
- size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
- : in.touchMajor;
- } else if (mRawPointerAxes.toolMajor.valid) {
+ mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor;
+ size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor)
+ : in.touchMajor;
+ } else if (mRawPointerAxes.toolMajor) {
touchMajor = toolMajor = in.toolMajor;
touchMinor = toolMinor =
- mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor)
- : in.toolMajor;
+ mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor;
+ size = mRawPointerAxes.toolMinor ? avg(in.toolMajor, in.toolMinor)
+ : in.toolMajor;
} else {
ALOG_ASSERT(false,
"No touch or tool axes. "
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 30c58a5..ef0e02f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -61,17 +61,17 @@
struct RawPointerAxes {
RawAbsoluteAxisInfo x{};
RawAbsoluteAxisInfo y{};
- RawAbsoluteAxisInfo pressure{};
- RawAbsoluteAxisInfo touchMajor{};
- RawAbsoluteAxisInfo touchMinor{};
- RawAbsoluteAxisInfo toolMajor{};
- RawAbsoluteAxisInfo toolMinor{};
- RawAbsoluteAxisInfo orientation{};
- RawAbsoluteAxisInfo distance{};
- RawAbsoluteAxisInfo tiltX{};
- RawAbsoluteAxisInfo tiltY{};
- RawAbsoluteAxisInfo trackingId{};
- RawAbsoluteAxisInfo slot{};
+ std::optional<RawAbsoluteAxisInfo> pressure{};
+ std::optional<RawAbsoluteAxisInfo> touchMajor{};
+ std::optional<RawAbsoluteAxisInfo> touchMinor{};
+ std::optional<RawAbsoluteAxisInfo> toolMajor{};
+ std::optional<RawAbsoluteAxisInfo> toolMinor{};
+ std::optional<RawAbsoluteAxisInfo> orientation{};
+ std::optional<RawAbsoluteAxisInfo> distance{};
+ std::optional<RawAbsoluteAxisInfo> tiltX{};
+ std::optional<RawAbsoluteAxisInfo> tiltY{};
+ std::optional<RawAbsoluteAxisInfo> trackingId{};
+ std::optional<RawAbsoluteAxisInfo> slot{};
inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
@@ -339,8 +339,8 @@
int32_t buttonState{};
// Scroll state.
- int32_t rawVScroll{};
- int32_t rawHScroll{};
+ float rawVScroll{};
+ float rawHScroll{};
inline void clear() { *this = RawState(); }
};
@@ -365,6 +365,16 @@
RawState mLastRawState;
CookedState mLastCookedState;
+ enum class ExternalStylusPresence {
+ // No external stylus connected.
+ NONE,
+ // An external stylus that can report touch/pressure that can be fused with the touchscreen.
+ TOUCH_FUSION,
+ // An external stylus that can only report buttons.
+ BUTTON_FUSION,
+ ftl_last = BUTTON_FUSION,
+ };
+ ExternalStylusPresence mExternalStylusPresence{ExternalStylusPresence::NONE};
// State provided by an external stylus
StylusState mExternalStylusState;
// If an external stylus is capable of reporting pointer-specific data like pressure, we will
@@ -460,8 +470,6 @@
float mTiltYCenter;
float mTiltYScale;
- bool mExternalStylusConnected;
-
// Oriented motion ranges for input device info.
struct OrientedRanges {
InputDeviceInfo::MotionRange x;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 24efae8..9a36bfb 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -36,6 +36,7 @@
#include <log/log_main.h>
#include <stats_pull_atom_callback.h>
#include <statslog.h>
+#include "InputReaderBase.h"
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
#include "gestures/HardwareProperties.h"
@@ -240,28 +241,28 @@
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
- RawAbsoluteAxisInfo slotAxisInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
- if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) {
+ if (std::optional<RawAbsoluteAxisInfo> slotAxis =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT);
+ slotAxis && slotAxis->maxValue >= 0) {
+ mMotionAccumulator.configure(deviceContext, slotAxis->maxValue + 1, true);
+ } else {
LOG(WARNING) << "Touchpad " << deviceContext.getName()
<< " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly.";
- slotAxisInfo.maxValue = 0;
+ mMotionAccumulator.configure(deviceContext, 1, true);
}
- mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
- mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+ mHardwareProperties = createHardwareProperties(deviceContext);
+ mGestureInterpreter->SetHardwareProperties(mHardwareProperties);
// Even though we don't explicitly delete copy/move semantics, it's safe to
// give away pointers to TouchpadInputMapper and its members here because
// 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
// 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
mGestureInterpreter->SetPropProvider(const_cast<GesturesPropProvider*>(&gesturePropProvider),
&mPropertyProvider);
- if (input_flags::enable_gestures_library_timer_provider()) {
- mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>(
- &kGestureTimerProvider),
- &mTimerProvider);
- }
+ mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>(
+ &kGestureTimerProvider),
+ &mTimerProvider);
mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
}
@@ -299,12 +300,8 @@
dump += addLinePrefix(mGestureConverter.dump(), INDENT4);
dump += INDENT3 "Gesture properties:\n";
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
- if (input_flags::enable_gestures_library_timer_provider()) {
- dump += INDENT3 "Timer provider:\n";
- dump += addLinePrefix(mTimerProvider.dump(), INDENT4);
- } else {
- dump += INDENT3 "Timer provider: disabled by flag\n";
- }
+ dump += INDENT3 "Timer provider:\n";
+ dump += addLinePrefix(mTimerProvider.dump(), INDENT4);
dump += INDENT3 "Captured event converter:\n";
dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
dump += StringPrintf(INDENT3 "DisplayId: %s\n",
@@ -377,6 +374,7 @@
.setBoolValues({config.touchpadTapDraggingEnabled});
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
+ mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState;
}
std::list<NotifyArgs> out;
if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
@@ -426,6 +424,9 @@
}
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
+ if (mTouchpadHardwareStateNotificationsEnabled) {
+ getPolicy()->notifyTouchpadHardwareState(*state, getDeviceId());
+ }
updatePalmDetectionMetrics();
return sendHardwareState(rawEvent.when, rawEvent.readTime, *state);
} else {
@@ -467,9 +468,6 @@
}
std::list<NotifyArgs> TouchpadInputMapper::timeoutExpired(nsecs_t when) {
- if (!input_flags::enable_gestures_library_timer_provider()) {
- return {};
- }
mTimerProvider.triggerCallbacks(when);
return processGestures(when, when);
}
@@ -482,6 +480,9 @@
return;
}
mGesturesToProcess.push_back(*gesture);
+ if (mTouchpadHardwareStateNotificationsEnabled) {
+ getPolicy()->notifyTouchpadGestureInfo(gesture->type, getDeviceId());
+ }
}
std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
@@ -501,4 +502,8 @@
return mDisplayId;
}
+std::optional<HardwareProperties> TouchpadInputMapper::getTouchpadHardwareProperties() {
+ return mHardwareProperties;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 8baa63e..a2c4be9 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -68,6 +68,8 @@
std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
+ std::optional<HardwareProperties> getTouchpadHardwareProperties() override;
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
@@ -92,6 +94,7 @@
HardwareStateConverter mStateConverter;
GestureConverter mGestureConverter;
CapturedTouchpadEventConverter mCapturedEventConverter;
+ HardwareProperties mHardwareProperties;
bool mPointerCaptured = false;
bool mResettingInterpreter = false;
@@ -112,6 +115,10 @@
std::optional<ui::LogicalDisplayId> mDisplayId;
nsecs_t mGestureStartTime{0};
+
+ // True if hardware state update notifications is available for usage based on its feature flag
+ // and settings value.
+ bool mTouchpadHardwareStateNotificationsEnabled = false;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
index f85cab2..5373440 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -16,18 +16,29 @@
#include "CursorScrollAccumulator.h"
+#include <android_companion_virtualdevice_flags.h>
#include "EventHub.h"
#include "InputDevice.h"
namespace android {
-CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) {
+namespace vd_flags = android::companion::virtualdevice::flags;
+
+CursorScrollAccumulator::CursorScrollAccumulator()
+ : mHaveRelWheel(false),
+ mHaveRelHWheel(false),
+ mHaveRelWheelHighRes(false),
+ mHaveRelHWheelHighRes(false) {
clearRelativeAxes();
}
void CursorScrollAccumulator::configure(InputDeviceContext& deviceContext) {
mHaveRelWheel = deviceContext.hasRelativeAxis(REL_WHEEL);
mHaveRelHWheel = deviceContext.hasRelativeAxis(REL_HWHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ mHaveRelWheelHighRes = deviceContext.hasRelativeAxis(REL_WHEEL_HI_RES);
+ mHaveRelHWheelHighRes = deviceContext.hasRelativeAxis(REL_HWHEEL_HI_RES);
+ }
}
void CursorScrollAccumulator::reset(InputDeviceContext& deviceContext) {
@@ -42,11 +53,31 @@
void CursorScrollAccumulator::process(const RawEvent& rawEvent) {
if (rawEvent.type == EV_REL) {
switch (rawEvent.code) {
+ case REL_WHEEL_HI_RES:
+ if (mHaveRelWheelHighRes) {
+ mRelWheel =
+ rawEvent.value / static_cast<float>(kEvdevHighResScrollUnitsPerDetent);
+ }
+ break;
+ case REL_HWHEEL_HI_RES:
+ if (mHaveRelHWheelHighRes) {
+ mRelHWheel =
+ rawEvent.value / static_cast<float>(kEvdevHighResScrollUnitsPerDetent);
+ }
+ break;
case REL_WHEEL:
- mRelWheel = rawEvent.value;
+ // We should ignore regular scroll events, if we have already have high-res scroll
+ // enabled.
+ if (!mHaveRelWheelHighRes) {
+ mRelWheel = rawEvent.value;
+ }
break;
case REL_HWHEEL:
- mRelHWheel = rawEvent.value;
+ // We should ignore regular scroll events, if we have already have high-res scroll
+ // enabled.
+ if (!mHaveRelHWheelHighRes) {
+ mRelHWheel = rawEvent.value;
+ }
break;
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index e563620..d3373cc 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -16,15 +16,12 @@
#pragma once
-#include <stdint.h>
-
namespace android {
class InputDeviceContext;
struct RawEvent;
/* Keeps track of cursor scrolling motions. */
-
class CursorScrollAccumulator {
public:
CursorScrollAccumulator();
@@ -37,19 +34,17 @@
inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
- inline int32_t getRelativeX() const { return mRelX; }
- inline int32_t getRelativeY() const { return mRelY; }
- inline int32_t getRelativeVWheel() const { return mRelWheel; }
- inline int32_t getRelativeHWheel() const { return mRelHWheel; }
+ inline float getRelativeVWheel() const { return mRelWheel; }
+ inline float getRelativeHWheel() const { return mRelHWheel; }
private:
bool mHaveRelWheel;
bool mHaveRelHWheel;
+ bool mHaveRelWheelHighRes;
+ bool mHaveRelHWheelHighRes;
- int32_t mRelX;
- int32_t mRelY;
- int32_t mRelWheel;
- int32_t mRelHWheel;
+ float mRelWheel;
+ float mRelHWheel;
void clearRelativeAxes();
};
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index 4919068..8dc6e4d 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -139,13 +139,11 @@
if (!mUsingSlotsProtocol) {
return;
}
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
+ if (const std::optional<int32_t> initialSlot = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT);
+ initialSlot.has_value()) {
+ mCurrentSlot = initialSlot.value();
} else {
- ALOGE("Could not retrieve current multi-touch slot index. status=%s",
- statusToString(status).c_str());
+ ALOGE("Could not retrieve current multi-touch slot index");
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
index 2b82ddf..4cf9243 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -26,13 +26,13 @@
}
void SingleTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
- mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X);
- mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y);
- mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE);
- mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH);
- mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE);
- mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X);
- mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y);
+ mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X).value_or(0);
+ mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y).value_or(0);
+ mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE).value_or(0);
+ mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH).value_or(0);
+ mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE).value_or(0);
+ mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X).value_or(0);
+ mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y).value_or(0);
}
void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index e8e7376..da2c683 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -60,16 +60,33 @@
}
}
+bool isGestureNoFocusChange(MotionClassification classification) {
+ switch (classification) {
+ case MotionClassification::TWO_FINGER_SWIPE:
+ case MotionClassification::MULTI_FINGER_SWIPE:
+ case MotionClassification::PINCH:
+ // Most gestures can be performed on an unfocused window, so they should not
+ // not affect window focus.
+ return true;
+ case MotionClassification::NONE:
+ case MotionClassification::AMBIGUOUS_GESTURE:
+ case MotionClassification::DEEP_PRESS:
+ return false;
+ }
+}
+
} // namespace
GestureConverter::GestureConverter(InputReaderContext& readerContext,
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
- deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
- deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
-}
+ mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
+ mEnableNoFocusChange(input_flags::enable_touchpad_no_focus_change()),
+ // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub
+ // won't classify a device as a touchpad if they're not present.
+ mXAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value()),
+ mYAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value()) {}
std::string GestureConverter::dump() const {
std::stringstream out;
@@ -337,7 +354,6 @@
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
- args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
}
float deltaX = gesture.details.scroll.dx;
@@ -352,7 +368,6 @@
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
- args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
return out;
}
@@ -426,7 +441,6 @@
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
- args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
mCurrentClassification = MotionClassification::NONE;
out += enterHover(when, readTime);
@@ -623,6 +637,18 @@
int32_t actionButton, int32_t buttonState,
uint32_t pointerCount,
const PointerCoords* pointerCoords) {
+ int32_t flags = 0;
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ if (mEnableNoFocusChange && isGestureNoFocusChange(mCurrentClassification)) {
+ flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+ if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
+ // This helps to make GestureDetector responsive.
+ flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+ }
+
return {mReaderContext.getNextId(),
when,
readTime,
@@ -632,7 +658,7 @@
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
- /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
+ flags,
mReaderContext.getGlobalMetaState(),
buttonState,
mCurrentClassification,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 829fb92..c9a35c1 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -99,6 +99,7 @@
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
const bool mEnableFlingStop;
+ const bool mEnableNoFocusChange;
std::optional<ui::LogicalDisplayId> mDisplayId;
FloatRect mBoundsInLogicalDisplay{};
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
index 04655dc..d8a1f50 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
@@ -16,6 +16,8 @@
#include "HardwareProperties.h"
+#include <optional>
+
namespace android {
namespace {
@@ -33,26 +35,34 @@
HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
HardwareProperties props;
- RawAbsoluteAxisInfo absMtPositionX;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub won't
+ // classify a device as a touchpad if they're not present.
+ RawAbsoluteAxisInfo absMtPositionX = context.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value();
props.left = absMtPositionX.minValue;
props.right = absMtPositionX.maxValue;
props.res_x = absMtPositionX.resolution;
- RawAbsoluteAxisInfo absMtPositionY;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ RawAbsoluteAxisInfo absMtPositionY = context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value();
props.top = absMtPositionY.minValue;
props.bottom = absMtPositionY.maxValue;
props.res_y = absMtPositionY.resolution;
- RawAbsoluteAxisInfo absMtOrientation;
- context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
- props.orientation_minimum = absMtOrientation.minValue;
- props.orientation_maximum = absMtOrientation.maxValue;
+ if (std::optional<RawAbsoluteAxisInfo> absMtOrientation =
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ absMtOrientation) {
+ props.orientation_minimum = absMtOrientation->minValue;
+ props.orientation_maximum = absMtOrientation->maxValue;
+ } else {
+ props.orientation_minimum = 0;
+ props.orientation_maximum = 0;
+ }
- RawAbsoluteAxisInfo absMtSlot;
- context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
- props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ if (std::optional<RawAbsoluteAxisInfo> absMtSlot = context.getAbsoluteAxisInfo(ABS_MT_SLOT);
+ absMtSlot) {
+ props.max_finger_cnt = absMtSlot->maxValue - absMtSlot->minValue + 1;
+ } else {
+ props.max_finger_cnt = 1;
+ }
props.max_touch_cnt = getMaxTouchCount(context);
// T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
@@ -71,9 +81,7 @@
// are haptic.
props.is_haptic_pad = false;
- RawAbsoluteAxisInfo absMtPressure;
- context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure);
- props.reports_pressure = absMtPressure.valid;
+ props.reports_pressure = context.hasAbsoluteAxis(ABS_MT_PRESSURE);
return props;
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 07e62c6..148ca5a 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -26,18 +26,13 @@
#include "accumulator/CursorButtonAccumulator.h"
#include "accumulator/MultiTouchMotionAccumulator.h"
#include "accumulator/TouchButtonAccumulator.h"
+#include "include/TouchpadHardwareState.h"
+#include "TouchpadHardwareState.h"
#include "include/gestures.h"
namespace android {
-// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have
-// to worry about where that memory is allocated.
-struct SelfContainedHardwareState {
- HardwareState state;
- std::vector<FingerState> fingers;
-};
-
// Converts RawEvents into the HardwareState structs used by the gestures library.
class HardwareStateConverter {
public:
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
index 2d5039a..e05e8e5 100644
--- a/services/inputflinger/rust/bounce_keys_filter.rs
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -17,12 +17,13 @@
//! Bounce keys input filter implementation.
//! Bounce keys is an accessibility feature to aid users who have physical disabilities, that
//! allows the user to configure the device to ignore rapid, repeated key presses of the same key.
-use crate::input_filter::Filter;
+use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID};
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 input::KeyboardType;
use log::debug;
use std::collections::{HashMap, HashSet};
@@ -42,7 +43,7 @@
next: Box<dyn Filter + Send + Sync>,
key_event_map: HashMap<i32, LastUpKeyEvent>,
blocked_events: Vec<BlockedEvent>,
- external_devices: HashSet<i32>,
+ supported_devices: HashSet<i32>,
bounce_key_threshold_ns: i64,
}
@@ -56,7 +57,7 @@
next,
key_event_map: HashMap::new(),
blocked_events: Vec::new(),
- external_devices: HashSet::new(),
+ supported_devices: HashSet::new(),
bounce_key_threshold_ns,
}
}
@@ -64,7 +65,10 @@
impl Filter for BounceKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
- if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
+ // Check if it is a supported device and event source contains Source::KEYBOARD
+ if !(self.supported_devices.contains(&event.deviceId)
+ && event.source.0 & Source::KEYBOARD.0 != 0)
+ {
self.next.notify_key(event);
return;
}
@@ -110,10 +114,17 @@
self.blocked_events.retain(|blocked_event| {
device_infos.iter().any(|x| blocked_event.device_id == x.deviceId)
});
- self.external_devices.clear();
+ self.supported_devices.clear();
for device_info in device_infos {
- if device_info.external {
- self.external_devices.insert(device_info.deviceId);
+ if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID {
+ continue;
+ }
+ if device_info.keyboardType == KeyboardType::None as i32 {
+ continue;
+ }
+ // Support Alphabetic keyboards and Non-alphabetic external keyboards
+ if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 {
+ self.supported_devices.insert(device_info.deviceId);
}
}
self.next.notify_devices_changed(device_infos);
@@ -122,16 +133,26 @@
fn destroy(&mut self) {
self.next.destroy();
}
+
+ fn dump(&mut self, dump_str: String) -> String {
+ let mut result = "Bounce Keys filter: \n".to_string();
+ result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns);
+ result += &format!("\tkey_event_map = {:?}\n", self.key_event_map);
+ result += &format!("\tblocked_events = {:?}\n", self.blocked_events);
+ result += &format!("\tsupported_devices = {:?}\n", self.supported_devices);
+ self.next.dump(dump_str + &result)
+ }
}
#[cfg(test)]
mod tests {
use crate::bounce_keys_filter::BounceKeysFilter;
- use crate::input_filter::{test_filter::TestFilter, Filter};
+ use crate::input_filter::{test_filter::TestFilter, Filter, VIRTUAL_KEYBOARD_DEVICE_ID};
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 input::KeyboardType;
static BASE_KEY_EVENT: KeyEvent = KeyEvent {
id: 1,
@@ -156,6 +177,7 @@
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
+ KeyboardType::Alphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
@@ -181,12 +203,103 @@
}
#[test]
- fn test_is_notify_key_doesnt_block_for_internal_keyboard() {
+ fn test_is_notify_key_for_tv_remote() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ KeyboardType::NonAlphabetic,
+ );
+
+ let source = Source(Source::KEYBOARD.0 | Source::DPAD.0);
+ let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event =
+ KeyEvent { eventTime: 100, action: KeyEventAction::UP, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event =
+ KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_blocks_for_internal_keyboard() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_internal_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ KeyboardType::Alphabetic,
+ );
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_doesnt_block_for_internal_non_alphabetic_keyboard() {
let next = TestFilter::new();
let mut filter = setup_filter_with_internal_device(
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
+ KeyboardType::NonAlphabetic,
+ );
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ 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_doesnt_block_for_virtual_keyboard() {
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_internal_device(
+ Box::new(next.clone()),
+ VIRTUAL_KEYBOARD_DEVICE_ID, /* device_id */
+ 100, /* threshold */
+ KeyboardType::Alphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
@@ -209,6 +322,7 @@
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
+ KeyboardType::NonAlphabetic,
);
let event =
@@ -233,12 +347,60 @@
let mut filter = setup_filter_with_devices(
Box::new(next.clone()),
&[
- DeviceInfo { deviceId: 1, external: true },
- DeviceInfo { deviceId: 2, external: true },
+ DeviceInfo {
+ deviceId: 1,
+ external: true,
+ keyboardType: KeyboardType::Alphabetic as i32,
+ },
+ DeviceInfo {
+ deviceId: 2,
+ external: true,
+ keyboardType: KeyboardType::Alphabetic as i32,
+ },
],
100, /* threshold */
);
+ // Bounce key scenario on the external keyboard
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { deviceId: 2, 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_and_internal_alphabetic_keyboards() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_devices(
+ Box::new(next.clone()),
+ &[
+ DeviceInfo {
+ deviceId: 1,
+ external: false,
+ keyboardType: KeyboardType::Alphabetic as i32,
+ },
+ DeviceInfo {
+ deviceId: 2,
+ external: true,
+ keyboardType: KeyboardType::Alphabetic as i32,
+ },
+ ],
+ 100, /* threshold */
+ );
+
+ // Bounce key scenario on the internal keyboard
let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
@@ -261,10 +423,15 @@
next: Box<dyn Filter + Send + Sync>,
device_id: i32,
threshold: i64,
+ keyboard_type: KeyboardType,
) -> BounceKeysFilter {
setup_filter_with_devices(
next,
- &[DeviceInfo { deviceId: device_id, external: true }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: true,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
@@ -273,10 +440,15 @@
next: Box<dyn Filter + Send + Sync>,
device_id: i32,
threshold: i64,
+ keyboard_type: KeyboardType,
) -> BounceKeysFilter {
setup_filter_with_devices(
next,
- &[DeviceInfo { deviceId: device_id, external: false }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: false,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 8b44af3..e221244 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -35,11 +35,15 @@
use log::{error, info};
use std::sync::{Arc, Mutex, RwLock};
+/// Virtual keyboard device ID
+pub const VIRTUAL_KEYBOARD_DEVICE_ID: i32 = -1;
+
/// Interface for all the sub input filters
pub trait Filter {
fn notify_key(&mut self, event: &KeyEvent);
fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]);
fn destroy(&mut self);
+ fn dump(&mut self, dump_str: String) -> String;
}
struct InputFilterState {
@@ -119,18 +123,30 @@
self.input_filter_thread.clone(),
));
state.enabled = true;
- info!("Slow keys filter is installed");
+ info!(
+ "Slow keys filter is installed, threshold = {:?}ns",
+ config.slowKeysThresholdNs
+ );
}
if config.bounceKeysThresholdNs > 0 {
first_filter =
Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
state.enabled = true;
- info!("Bounce keys filter is installed");
+ info!(
+ "Bounce keys filter is installed, threshold = {:?}ns",
+ config.bounceKeysThresholdNs
+ );
}
state.first_filter = first_filter;
}
Result::Ok(())
}
+
+ fn dumpFilter(&self) -> binder::Result<String> {
+ let first_filter = &mut self.state.lock().unwrap().first_filter;
+ let dump_str = first_filter.dump(String::new());
+ Result::Ok(dump_str)
+ }
}
struct BaseFilter {
@@ -158,6 +174,11 @@
fn destroy(&mut self) {
// do nothing
}
+
+ fn dump(&mut self, dump_str: String) -> String {
+ // do nothing
+ dump_str
+ }
}
/// This struct wraps around IInputFilterCallbacks restricting access to only
@@ -214,6 +235,7 @@
InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
KeyEventAction::KeyEventAction,
};
+ use input::KeyboardType;
use std::sync::{Arc, RwLock};
#[test]
@@ -256,7 +278,11 @@
Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
);
assert!(input_filter
- .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }])
+ .notifyInputDevicesChanged(&[DeviceInfo {
+ deviceId: 0,
+ external: true,
+ keyboardType: KeyboardType::None as i32
+ }])
.is_ok());
assert!(test_filter.is_device_changed_called());
}
@@ -389,6 +415,10 @@
fn destroy(&mut self) {
self.inner().is_destroy_called = true;
}
+ fn dump(&mut self, dump_str: String) -> String {
+ // do nothing
+ dump_str
+ }
}
}
diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
index 0f18a2f..8830aac 100644
--- a/services/inputflinger/rust/slow_keys_filter.rs
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -18,12 +18,13 @@
//! 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::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID};
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 input::KeyboardType;
use log::debug;
use std::collections::HashSet;
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
@@ -41,7 +42,7 @@
struct SlowKeysFilterInner {
next: Box<dyn Filter + Send + Sync>,
slow_key_threshold_ns: i64,
- external_devices: HashSet<i32>,
+ supported_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>,
@@ -65,7 +66,7 @@
let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner {
next,
slow_key_threshold_ns,
- external_devices: HashSet::new(),
+ supported_devices: HashSet::new(),
pending_down_events: Vec::new(),
ongoing_down_events: Vec::new(),
input_filter_thread: input_filter_thread.clone(),
@@ -98,8 +99,8 @@
{
// acquire write lock
let mut slow_filter = self.write_inner();
- if !(slow_filter.external_devices.contains(&event.deviceId)
- && event.source == Source::KEYBOARD)
+ if !(slow_filter.supported_devices.contains(&event.deviceId)
+ && event.source.0 & Source::KEYBOARD.0 != 0)
{
slow_filter.next.notify_key(event);
return;
@@ -164,10 +165,17 @@
slow_filter
.ongoing_down_events
.retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId));
- slow_filter.external_devices.clear();
+ slow_filter.supported_devices.clear();
for device_info in device_infos {
- if device_info.external {
- slow_filter.external_devices.insert(device_info.deviceId);
+ if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID {
+ continue;
+ }
+ if device_info.keyboardType == KeyboardType::None as i32 {
+ continue;
+ }
+ // Support Alphabetic keyboards and Non-alphabetic external keyboards
+ if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 {
+ slow_filter.supported_devices.insert(device_info.deviceId);
}
}
slow_filter.next.notify_devices_changed(device_infos);
@@ -178,6 +186,16 @@
slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone()));
slow_filter.next.destroy();
}
+
+ fn dump(&mut self, dump_str: String) -> String {
+ let mut slow_filter = self.write_inner();
+ let mut result = "Slow Keys filter: \n".to_string();
+ result += &format!("\tthreshold = {:?}ns\n", slow_filter.slow_key_threshold_ns);
+ result += &format!("\tongoing_down_events = {:?}\n", slow_filter.ongoing_down_events);
+ result += &format!("\tpending_down_events = {:?}\n", slow_filter.pending_down_events);
+ result += &format!("\tsupported_devices = {:?}\n", slow_filter.supported_devices);
+ slow_filter.next.dump(dump_str + &result)
+ }
}
impl ThreadCallback for SlowKeysFilter {
@@ -217,6 +235,7 @@
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use input::KeyboardType;
use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
use std::sync::{Arc, RwLock};
use std::time::Duration;
@@ -240,7 +259,7 @@
static SLOW_KEYS_THRESHOLD_NS: i64 = 100 * 1000000; // 100 ms
#[test]
- fn test_is_notify_key_for_internal_keyboard_not_blocked() {
+ fn test_is_notify_key_for_internal_non_alphabetic_keyboard_not_blocked() {
let test_callbacks = TestCallbacks::new();
let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
@@ -249,6 +268,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::NonAlphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
@@ -266,6 +286,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::NonAlphabetic,
);
let event =
@@ -275,6 +296,115 @@
}
#[test]
+ fn test_notify_key_for_tv_remote_when_key_pressed_for_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = get_thread(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 */
+ SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::NonAlphabetic,
+ );
+ let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ let source = Source(Source::KEYBOARD.0 | Source::DPAD.0);
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time,
+ eventTime: down_time,
+ source,
+ ..BASE_KEY_EVENT
+ });
+ assert!(next.last_event().is_none());
+
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ source,
+ policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
+ ..BASE_KEY_EVENT
+ }
+ );
+
+ let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time,
+ eventTime: up_time,
+ source,
+ ..BASE_KEY_EVENT
+ });
+
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: up_time,
+ source,
+ ..BASE_KEY_EVENT
+ }
+ );
+ }
+
+ #[test]
+ fn test_notify_key_for_internal_alphabetic_keyboard_when_key_pressed_for_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = get_thread(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 */
+ SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
+ );
+ let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time,
+ eventTime: down_time,
+ ..BASE_KEY_EVENT
+ });
+ assert!(next.last_event().is_none());
+
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
+ ..BASE_KEY_EVENT
+ }
+ );
+
+ let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ });
+
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ }
+ );
+ }
+
+ #[test]
fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() {
let test_callbacks = TestCallbacks::new();
let test_thread = get_thread(test_callbacks.clone());
@@ -284,6 +414,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
);
let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
filter.notify_key(&KeyEvent {
@@ -335,6 +466,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
);
let mut now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
filter.notify_key(&KeyEvent {
@@ -367,6 +499,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
);
let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
@@ -388,11 +521,16 @@
test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
+ keyboard_type: KeyboardType,
) -> SlowKeysFilter {
setup_filter_with_devices(
next,
test_thread,
- &[DeviceInfo { deviceId: device_id, external: true }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: true,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
@@ -402,11 +540,16 @@
test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
+ keyboard_type: KeyboardType,
) -> SlowKeysFilter {
setup_filter_with_devices(
next,
test_thread,
- &[DeviceInfo { deviceId: device_id, external: false }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: false,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 6c7c7fb..161a5fc 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -134,6 +134,14 @@
fn destroy(&mut self) {
self.next.destroy();
}
+
+ fn dump(&mut self, dump_str: String) -> String {
+ let mut result = "Sticky Keys filter: \n".to_string();
+ result += &format!("\tmodifier_state = {:?}\n", self.modifier_state);
+ result += &format!("\tlocked_modifier_state = {:?}\n", self.locked_modifier_state);
+ result += &format!("\tcontributing_devices = {:?}\n", self.contributing_devices);
+ self.next.dump(dump_str + &result)
+ }
}
fn is_modifier_key(keycode: i32) -> bool {
@@ -235,6 +243,7 @@
DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use input::KeyboardType;
use input::ModifierState;
use std::sync::{Arc, RwLock};
@@ -496,7 +505,11 @@
..BASE_KEY_UP
});
- sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
+ sticky_keys_filter.notify_devices_changed(&[DeviceInfo {
+ deviceId: 2,
+ external: true,
+ keyboardType: KeyboardType::Alphabetic as i32,
+ }]);
assert_eq!(
test_callbacks.get_last_modifier_state(),
ModifierState::CtrlLeftOn | ModifierState::CtrlOn
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 18b6c5e..744cf4a 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -70,17 +70,21 @@
"InputTraceSession.cpp",
"InputTracingTest.cpp",
"InstrumentedInputReader.cpp",
+ "JoystickInputMapper_test.cpp",
"LatencyTracker_test.cpp",
"MultiTouchMotionAccumulator_test.cpp",
"NotifyArgs_test.cpp",
"PointerChoreographer_test.cpp",
"PreferStylusOverTouch_test.cpp",
"PropertyProvider_test.cpp",
+ "RotaryEncoderInputMapper_test.cpp",
"SlopController_test.cpp",
+ "SwitchInputMapper_test.cpp",
"SyncQueue_test.cpp",
"TimerProvider_test.cpp",
"TestInputListener.cpp",
"TouchpadInputMapper_test.cpp",
+ "VibratorInputMapper_test.cpp",
"MultiTouchInputMapper_test.cpp",
"KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
@@ -115,6 +119,9 @@
test_options: {
unit_test: true,
},
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "device-platinum-tests",
+ ],
native_coverage: false,
}
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index b738abf..353011a 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -20,6 +20,7 @@
#include <memory>
#include <EventHub.h>
+#include <com_android_input_flags.h>
#include <gtest/gtest.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
@@ -32,9 +33,14 @@
#include "TestEventMatchers.h"
#include "TestInputListener.h"
+namespace input_flags = com::android::input::flags;
+
namespace android {
using testing::AllOf;
+using testing::Each;
+using testing::ElementsAre;
+using testing::VariantWith;
class CapturedTouchpadEventConverterTest : public testing::Test {
public:
@@ -44,6 +50,8 @@
mReader(mFakeEventHub, mFakePolicy, mFakeListener),
mDevice(newDevice()),
mDeviceContext(*mDevice, EVENTHUB_ID) {
+ input_flags::include_relative_axis_values_for_captured_touchpads(true);
+
const size_t slotCount = 8;
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
@@ -123,7 +131,7 @@
TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populatedCorrectly) {
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 2000, 0, 0, 40);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 1100, 0, 0, 35);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 1000, 0, 0, 30);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 900, 0, 0, 25);
@@ -147,8 +155,8 @@
const InputDeviceInfo::MotionRange* posY =
info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD);
ASSERT_NE(nullptr, posY);
- EXPECT_NEAR(0, posY->min, EPSILON);
- EXPECT_NEAR(2500, posY->max, EPSILON);
+ EXPECT_NEAR(-500, posY->min, EPSILON);
+ EXPECT_NEAR(2000, posY->max, EPSILON);
EXPECT_NEAR(40, posY->resolution, EPSILON);
const InputDeviceInfo::MotionRange* touchMajor =
@@ -179,8 +187,22 @@
EXPECT_NEAR(800, toolMinor->max, EPSILON);
EXPECT_NEAR(20, toolMinor->resolution, EPSILON);
- // ...except orientation and pressure, which get scaled, and size, which is generated from other
- // values.
+ // ...except for the relative motion axes, derived from the corresponding absolute ones:
+ const InputDeviceInfo::MotionRange* relX =
+ info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, relX);
+ EXPECT_NEAR(-4000, relX->min, EPSILON);
+ EXPECT_NEAR(4000, relX->max, EPSILON);
+ EXPECT_NEAR(45, relX->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* relY =
+ info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, relY);
+ EXPECT_NEAR(-2500, relY->min, EPSILON);
+ EXPECT_NEAR(2500, relY->max, EPSILON);
+ EXPECT_NEAR(40, relY->resolution, EPSILON);
+
+ // ...orientation and pressure, which get scaled:
const InputDeviceInfo::MotionRange* orientation =
info.getMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, AINPUT_SOURCE_TOUCHPAD);
ASSERT_NE(nullptr, orientation);
@@ -195,6 +217,7 @@
EXPECT_NEAR(1, pressure->max, EPSILON);
EXPECT_NEAR(0, pressure->resolution, EPSILON);
+ // ... and size, which is generated from other values.
const InputDeviceInfo::MotionRange* size =
info.getMotionRange(AMOTION_EVENT_AXIS_SIZE, AINPUT_SOURCE_TOUCHPAD);
ASSERT_NE(nullptr, size);
@@ -216,7 +239,9 @@
// present, since it's generated from axes that aren't provided by this device).
EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHPAD));
EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD));
- EXPECT_EQ(2u, info.getMotionRanges().size());
+ EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD));
+ EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD));
+ EXPECT_EQ(4u, info.getMotionRanges().size());
}
TEST_F(CapturedTouchpadEventConverterTest, OneFinger_motionReportedCorrectly) {
@@ -232,28 +257,31 @@
EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
- WithCoords(50, 100), WithToolType(ToolType::FINGER)));
+ WithCoords(50, 100), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER)));
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 99);
EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
- WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+ WithCoords(52, 99), WithRelativeMotion(2, -1),
+ WithToolType(ToolType::FINGER)));
processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
processAxis(conv, EV_KEY, BTN_TOUCH, 0);
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
- WithCoords(52, 99), WithToolType(ToolType::FINGER)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u),
- WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(52, 99), WithRelativeMotion(0, 0), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER)))));
}
TEST_F(CapturedTouchpadEventConverterTest, OneFinger_touchDimensionsPassedThrough) {
@@ -504,13 +532,13 @@
EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
- WithCoords(51, 100)));
+ WithCoords(51, 100), WithRelativeMotion(0, 0)));
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
- WithCoords(52, 100)));
+ WithCoords(52, 100), WithRelativeMotion(1, 0)));
}
TEST_F(CapturedTouchpadEventConverterTest, FingerArrivingAfterPalm_onlyFingerReported) {
@@ -550,7 +578,7 @@
EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
- WithCoords(98, 148)));
+ WithCoords(98, 148), WithRelativeMotion(-2, -2)));
}
TEST_F(CapturedTouchpadEventConverterTest, FingerAndFingerTurningIntoPalm_partiallyCancelled) {
@@ -572,17 +600,17 @@
processAxis(conv, EV_KEY, BTN_TOUCH, 1);
processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u), WithPointerToolType(0, ToolType::FINGER),
- WithPointerToolType(1, ToolType::FINGER)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u),
+ WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)))));
processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
@@ -591,15 +619,16 @@
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251);
processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithFlags(AMOTION_EVENT_FLAG_CANCELED), WithPointerCount(2u)));
+ std::list<NotifyArgs> args = processSync(conv);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithPointerCount(2u))));
}
TEST_F(CapturedTouchpadEventConverterTest, FingerAndPalmTurningIntoFinger_reported) {
@@ -632,15 +661,15 @@
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251);
processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPointerCount(1u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u)))));
}
TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) {
@@ -656,7 +685,8 @@
EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
- WithCoords(50, 100), WithToolType(ToolType::FINGER)));
+ WithCoords(50, 100), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER)));
processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
@@ -670,18 +700,22 @@
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
- WithCoords(52, 99), WithToolType(ToolType::FINGER)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u), WithPointerCoords(0, 52, 99),
- WithPointerCoords(1, 250, 200), WithPointerToolType(0, ToolType::FINGER),
- WithPointerToolType(1, ToolType::FINGER)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPointerCount(1u), WithCoords(52, 99),
+ WithRelativeMotion(2, -1),
+ WithToolType(ToolType::FINGER))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerCoords(0, 52, 99),
+ WithPointerRelativeMotion(0, 0, 0),
+ WithPointerCoords(1, 250, 200),
+ WithPointerRelativeMotion(1, 0, 0),
+ WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)))));
processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
@@ -692,34 +726,96 @@
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
- args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u),
- WithPointerCoords(0, 52, 99), WithPointerCoords(1, 255, 202),
- WithPointerToolType(1, ToolType::FINGER),
- WithPointerToolType(0, ToolType::FINGER)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u), WithPointerCoords(0, 52, 99),
- WithPointerCoords(1, 255, 202), WithPointerToolType(0, ToolType::FINGER),
- WithPointerToolType(1, ToolType::FINGER)));
+ std::list<NotifyArgs> args = processSync(conv);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPointerRelativeMotion(1, 5, 2))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerRelativeMotion(1, 0, 0)))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithPointerCount(2u), WithPointerCoords(0, 52, 99),
+ WithPointerRelativeMotion(0, 0, 0), WithPointerCoords(1, 255, 202),
+ WithPointerToolType(1, ToolType::FINGER),
+ WithPointerToolType(0, ToolType::FINGER)))));
processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
processAxis(conv, EV_KEY, BTN_TOUCH, 0);
args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithPointerCount(1u), WithCoords(255, 202),
+ WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER)))));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, RelativeMotionAxesClearedForNewFingerInSlot) {
+ CapturedTouchpadEventConverter conv = createConverter();
+ // Put down one finger.
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(50, 100), WithRelativeMotion(0, 0)));
+
+ // Move it in negative X and Y directions.
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 47);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 97);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(47, 97),
+ WithRelativeMotion(-3, -3)));
+
+ // Lift it.
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(47, 97),
+ WithRelativeMotion(0, 0),
+ WithPointerCount(1u)))));
+
+ // Put down another finger using the same slot. Relative axis values should be cleared.
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 60);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 60);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(60, 60), WithRelativeMotion(0, 0)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 64);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 58);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
- WithCoords(255, 202), WithToolType(ToolType::FINGER)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u),
- WithCoords(255, 202), WithToolType(ToolType::FINGER)));
+ WithCoords(64, 58), WithRelativeMotion(4, -2)));
}
// Pointer IDs max out at 31, and so must be reused once a touch is lifted to avoid running out.
@@ -737,17 +833,18 @@
processAxis(conv, EV_KEY, BTN_TOUCH, 1);
processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
- WithPointerId(/*index=*/0, /*id=*/0)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0),
- WithPointerId(/*index=*/1, /*id=*/1)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithPointerCount(1u),
+ WithPointerId(/*index=*/0, /*id=*/0))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u),
+ WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1)))));
// Lift the finger in slot 0, freeing up pointer ID 0...
processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
@@ -758,27 +855,30 @@
processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 3);
processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 30);
- args = processSync(conv);
- ASSERT_EQ(3u, args.size());
+ std::list<NotifyArgs> args = processSync(conv);
// Slot 1 being present will result in a MOVE event, even though it hasn't actually moved (see
// comments in CapturedTouchpadEventConverter::sync).
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u),
- WithPointerId(/*index=*/0, /*id=*/0), WithPointerId(/*index=*/1, /*id=*/1)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0),
- WithPointerId(/*index=*/1, /*id=*/1)));
- args.pop_front();
- // Slot 0 being lifted causes the finger from slot 1 to move up to index 0, but keep its
- // previous ID. The new finger in slot 2 should take ID 0, which was just freed up.
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/1),
- WithPointerId(/*index=*/1, /*id=*/0)));
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1))),
+ // Slot 0 being lifted causes the finger from slot 1 to move up to index
+ // 0, but keep its previous ID. The new finger in slot 2 should take ID
+ // 0, which was just freed up.
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerId(/*index=*/0, /*id=*/1),
+ WithPointerId(/*index=*/1, /*id=*/0)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithPointerCount(2u))));
}
// Motion events without any pointers are invalid, so when a button press is reported in the same
@@ -797,33 +897,30 @@
processAxis(conv, EV_KEY, BTN_LEFT, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u),
- WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithPointerCount(1u), WithCoords(50, 100),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)))));
processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
processAxis(conv, EV_KEY, BTN_TOUCH, 0);
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
processAxis(conv, EV_KEY, BTN_LEFT, 0);
- args = processSync(conv);
- ASSERT_EQ(3u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u),
- WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(0)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_UP));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithPointerCount(1u), WithCoords(50, 100),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0))),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_UP))));
}
// Some touchpads sometimes report a button press before they report the finger touching the pad. In
@@ -841,15 +938,14 @@
processAxis(conv, EV_KEY, BTN_TOUCH, 1);
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u),
- WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithPointerCount(1u), WithCoords(50, 100),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)))));
}
// When all fingers are lifted from a touchpad, we should release any buttons that are down, since
@@ -866,29 +962,25 @@
processAxis(conv, EV_KEY, BTN_LEFT, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS))));
processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
processAxis(conv, EV_KEY, BTN_TOUCH, 0);
processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
- args = processSync(conv);
- ASSERT_EQ(3u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u),
- WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(0)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_UP));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithPointerCount(1u), WithCoords(50, 100),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0))),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_UP))));
processAxis(conv, EV_KEY, BTN_LEFT, 0);
ASSERT_EQ(0u, processSync(conv).size());
@@ -908,48 +1000,41 @@
WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
processAxis(conv, EV_KEY, BTN_LEFT, 1);
- std::list<NotifyArgs> args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)))));
processAxis(conv, EV_KEY, BTN_RIGHT, 1);
- args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
- AMOTION_EVENT_BUTTON_SECONDARY)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY)))));
processAxis(conv, EV_KEY, BTN_LEFT, 0);
- args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY)))));
processAxis(conv, EV_KEY, BTN_RIGHT, 0);
- args = processSync(conv);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0)));
+ EXPECT_THAT(processSync(conv),
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(0)))));
}
} // namespace android
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index cda067f..b27d02d 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -17,11 +17,13 @@
#include "CursorInputMapper.h"
#include <list>
+#include <optional>
#include <string>
#include <tuple>
#include <variant>
#include <android-base/logging.h>
+#include <android_companion_virtualdevice_flags.h>
#include <com_android_input_flags.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
@@ -92,41 +94,10 @@
return v;
}
-/**
- * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
- *
- * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
- * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
- * device doesn't set its associated viewport when it's configured.
- *
- * TODO(b/319217713): work out a way to avoid this fake.
- */
-class ViewportFakingInputDeviceContext : public InputDeviceContext {
-public:
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- std::optional<DisplayViewport> viewport)
- : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
-
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- ui::Rotation orientation)
- : ViewportFakingInputDeviceContext(device, eventHubId,
- createPrimaryViewport(orientation)) {}
-
- std::optional<DisplayViewport> getAssociatedViewport() const override {
- return mAssociatedViewport;
- }
-
- void setViewport(const std::optional<DisplayViewport>& viewport) {
- mAssociatedViewport = viewport;
- }
-
-private:
- std::optional<DisplayViewport> mAssociatedViewport;
-};
-
} // namespace
namespace input_flags = com::android::input::flags;
+namespace vd_flags = android::companion::virtualdevice::flags;
/**
* Unit tests for CursorInputMapper.
@@ -151,6 +122,10 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
.WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
@@ -193,6 +168,7 @@
protected:
void SetUp() override {
input_flags::enable_new_mouse_pointer_ballistics(false);
+ vd_flags::high_resolution_scroll(false);
CursorInputMapperUnitTestBase::SetUp();
}
};
@@ -534,8 +510,9 @@
// need to be rotated.
mPropertyMap.addProperty("cursor.mode", "navigation");
mPropertyMap.addProperty("cursor.orientationAware", "1");
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
@@ -551,8 +528,9 @@
// Since InputReader works in the un-rotated coordinate space, only devices that are not
// orientation-aware are affected by display rotation.
mPropertyMap.addProperty("cursor.mode", "navigation");
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0)));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
@@ -563,7 +541,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
std::list<NotifyArgs> args =
mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
@@ -576,7 +555,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1));
@@ -588,7 +568,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0));
@@ -642,8 +623,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
// Ensure input events are generated for the secondary display.
@@ -663,8 +644,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the primary display as the display on which the pointer should be shown.
// Associate the InputDevice with the secondary display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
// With PointerChoreographer enabled, there could be a PointerController for the associated
// display even if it is different from the pointer display. So the mapper should generate an
@@ -835,6 +816,72 @@
WithOrientation(0.0f), WithDistance(0.0f)))));
}
+TEST_F(CursorInputMapperUnitTest, ProcessRegularScroll) {
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(1.0f, 1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
/**
* When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
* pointer acceleration or speed processing should not be applied.
@@ -954,8 +1001,8 @@
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -993,9 +1040,8 @@
mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
// Don't associate the device with the display yet.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID,
- /*viewport=*/std::nullopt);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -1009,7 +1055,7 @@
ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f);
// Now associate the device with the display, and verify that acceleration is disabled.
- deviceContext.setViewport(primaryViewport);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport));
args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
args.clear();
@@ -1023,6 +1069,72 @@
WithRelativeMotion(10, 20)))));
}
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessRegularScroll) {
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(1.0f, 1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessHighResScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, HighResScrollIgnoresRegularScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
namespace {
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 2e296da..0e3d15a 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -48,9 +48,6 @@
case EventHubInterface::DEVICE_REMOVED:
ALOGI("Device removed: %i", event.deviceId);
break;
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- ALOGI("Finished device scan.");
- break;
}
} else {
ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
@@ -145,15 +142,13 @@
// None of the existing system devices should be changing while this test is run.
// Check that the returned device ids are unique for all of the existing devices.
EXPECT_EQ(existingDevices.size(), events.size() - 1);
- // The last event should be "finished device scan"
- EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
}
int32_t EventHubTest::waitForDeviceCreation() {
// Wait a little longer than usual, to ensure input device has time to be created
std::vector<RawEvent> events = getEvents(2);
- if (events.size() != 2) {
- ADD_FAILURE() << "Instead of 2 events, received " << events.size();
+ if (events.size() != 1) {
+ ADD_FAILURE() << "Instead of 1 event, received " << events.size();
return 0; // this value is unused
}
const RawEvent& deviceAddedEvent = events[0];
@@ -161,21 +156,15 @@
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
const int32_t deviceId = deviceAddedEvent.deviceId;
EXPECT_EQ(identifier.name, mKeyboard->getName());
- const RawEvent& finishedDeviceScanEvent = events[1];
- EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
- finishedDeviceScanEvent.type);
return deviceId;
}
void EventHubTest::waitForDeviceClose(int32_t deviceId) {
std::vector<RawEvent> events = getEvents(2);
- ASSERT_EQ(2U, events.size());
+ ASSERT_EQ(1U, events.size());
const RawEvent& deviceRemovedEvent = events[0];
EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
- const RawEvent& finishedDeviceScanEvent = events[1];
- EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
- finishedDeviceScanEvent.type);
}
void EventHubTest::assertNoMoreEvents() {
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index daa000f..943de6e 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -16,6 +16,8 @@
#include "FakeEventHub.h"
+#include <optional>
+
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include <linux/input-event-codes.h>
@@ -86,10 +88,6 @@
return device->disable();
}
-void FakeEventHub::finishDeviceScan() {
- enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
-}
-
void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
getDevice(deviceId)->configuration.addProperty(key, value);
}
@@ -103,7 +101,6 @@
Device* device = getDevice(deviceId);
RawAbsoluteAxisInfo info;
- info.valid = true;
info.minValue = minValue;
info.maxValue = maxValue;
info.flat = flat;
@@ -154,9 +151,10 @@
getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
}
-void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+void FakeEventHub::setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const {
Device* device = getDevice(deviceId);
- device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+ device->keyRemapping = keyRemapping;
}
void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
@@ -263,18 +261,16 @@
return device->configuration;
}
-status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
+std::optional<RawAbsoluteAxisInfo> FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->absoluteAxes.indexOfKey(axis);
if (index >= 0) {
- *outAxisInfo = device->absoluteAxes.valueAt(index);
- return OK;
+ return device->absoluteAxes.valueAt(index);
}
}
- outAxisInfo->clear();
- return -1;
+ return std::nullopt;
}
bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
@@ -417,18 +413,15 @@
return AKEY_STATE_UNKNOWN;
}
-status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const {
+std::optional<int32_t> FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
if (index >= 0) {
- *outValue = device->absoluteAxisValue.valueAt(index);
- return OK;
+ return device->absoluteAxisValue.valueAt(index);
}
}
- *outValue = 0;
- return -1;
+ return std::nullopt;
}
void FakeEventHub::setMtSlotValues(int32_t deviceId, int32_t axis,
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index f07b344..2dfbb23 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -55,7 +55,7 @@
KeyedVector<int32_t, int32_t> absoluteAxisValue;
KeyedVector<int32_t, KeyInfo> keysByScanCode;
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
- std::unordered_map<int32_t, int32_t> keyRemapping;
+ std::map<int32_t, int32_t> keyRemapping;
KeyedVector<int32_t, bool> leds;
// fake mapping which would normally come from keyCharacterMap
std::unordered_map<int32_t, int32_t> keyCodeMapping;
@@ -112,8 +112,6 @@
status_t enableDevice(int32_t deviceId) override;
status_t disableDevice(int32_t deviceId) override;
- void finishDeviceScan();
-
void addConfigurationProperty(int32_t deviceId, const char* key, const char* value);
void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration);
@@ -131,7 +129,7 @@
void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
uint32_t flags);
void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
+ void setKeyRemapping(int32_t deviceId, const std::map<int32_t, int32_t>& keyRemapping) const;
void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
@@ -168,8 +166,8 @@
InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
int32_t getDeviceControllerNumber(int32_t) const override;
std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override;
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override;
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const override;
bool hasRelativeAxis(int32_t deviceId, int axis) const override;
bool hasInputProperty(int32_t, int) const override;
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
@@ -187,7 +185,7 @@
std::optional<RawLayoutInfo> getRawLayoutInfo(int32_t deviceId) const override;
int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override;
+ std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const override;
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
// Return true if the device has non-empty key layout.
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index 3df05f4..db68d8a 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -54,13 +54,6 @@
ASSERT_EQ(nullptr, mFilteredEvent);
}
-void FakeInputDispatcherPolicy::assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mConfigurationChangedTime) << "Timed out waiting for configuration changed call";
- ASSERT_EQ(*mConfigurationChangedTime, when);
- mConfigurationChangedTime = std::nullopt;
-}
-
void FakeInputDispatcherPolicy::assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
std::scoped_lock lock(mLock);
ASSERT_TRUE(mLastNotifySwitch);
@@ -342,11 +335,6 @@
return std::make_optional(item);
}
-void FakeInputDispatcherPolicy::notifyConfigurationChanged(nsecs_t when) {
- std::scoped_lock lock(mLock);
- mConfigurationChangedTime = when;
-}
-
void FakeInputDispatcherPolicy::notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
std::optional<gui::Pid> pid,
const std::string&) {
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index a0f3ea9..a9e39d1 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -66,7 +66,6 @@
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args);
void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point);
void assertFilterInputEventWasNotCalled();
- void assertNotifyConfigurationChangedWasCalled(nsecs_t when);
void assertNotifySwitchWasCalled(const NotifySwitchArgs& args);
void assertOnPointerDownEquals(const sp<IBinder>& touchedToken);
void assertOnPointerDownWasNotCalled();
@@ -121,7 +120,6 @@
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
- std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock);
sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
@@ -173,7 +171,6 @@
std::condition_variable& condition)
REQUIRES(mLock);
- void notifyConfigurationChanged(nsecs_t when) override;
void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
const std::string&) override;
void notifyWindowResponsive(const sp<IBinder>& connectionToken,
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index d2cb0ac..f373cac 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -16,6 +16,7 @@
#include "FakeInputReaderPolicy.h"
+#include <android-base/properties.h>
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
@@ -24,20 +25,30 @@
namespace android {
+namespace {
+
+static const int HW_TIMEOUT_MULTIPLIER = base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+
+} // namespace
+
void FakeInputReaderPolicy::assertInputDevicesChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (!devicesChanged) {
- FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
- }
- });
+ waitForInputDevices(
+ [](bool devicesChanged) {
+ if (!devicesChanged) {
+ FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+ }
+ },
+ ADD_INPUT_DEVICE_TIMEOUT);
}
void FakeInputReaderPolicy::assertInputDevicesNotChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (devicesChanged) {
- FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
- }
- });
+ waitForInputDevices(
+ [](bool devicesChanged) {
+ if (devicesChanged) {
+ FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+ }
+ },
+ INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT);
}
void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) {
@@ -58,6 +69,17 @@
ASSERT_FALSE(mDeviceIdOfNotifiedStylusGesture);
}
+void FakeInputReaderPolicy::assertTouchpadHardwareStateNotified() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ const bool success =
+ mTouchpadHardwareStateNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mTouchpadHardwareState.has_value();
+ });
+ ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified";
+}
+
void FakeInputReaderPolicy::clearViewports() {
mViewports.clear();
mConfig.setDisplayViewports(mViewports);
@@ -227,6 +249,17 @@
mDevicesChangedCondition.notify_all();
}
+void FakeInputReaderPolicy::notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ mTouchpadHardwareState = schs;
+ mTouchpadHardwareStateNotified.notify_all();
+}
+
+void FakeInputReaderPolicy::notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+}
+
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
@@ -236,14 +269,16 @@
return "";
}
-void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged,
+ std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
const bool devicesChanged =
- mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
- return mInputDevicesChanged;
- });
+ mDevicesChangedCondition.wait_for(lock, timeout * HW_TIMEOUT_MULTIPLIER,
+ [this]() REQUIRES(mLock) {
+ return mInputDevicesChanged;
+ });
ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
mInputDevicesChanged = false;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 94f1311..3a2b4e9 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -42,6 +42,7 @@
void assertInputDevicesNotChanged();
void assertStylusGestureNotified(int32_t deviceId);
void assertStylusGestureNotNotified();
+ void assertTouchpadHardwareStateNotified();
virtual void clearViewports();
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
@@ -82,10 +83,14 @@
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+ void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) override;
+ void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
- void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
+ void waitForInputDevices(std::function<void(bool)> processDevicesChanged,
+ std::chrono::milliseconds timeout);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
mutable std::mutex mLock;
@@ -101,6 +106,9 @@
std::condition_variable mStylusGestureNotifiedCondition;
std::optional<DeviceId> mDeviceIdOfNotifiedStylusGesture GUARDED_BY(mLock){};
+ std::condition_variable mTouchpadHardwareStateNotified;
+ std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){};
+
uint32_t mNextPointerCaptureSequenceNumber{0};
};
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index d0998ba..887a939 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -148,12 +148,6 @@
return mIsPointerShown;
}
-std::optional<FloatRect> FakePointerController::getBounds() const {
- if (!mEnabled) return std::nullopt;
-
- return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt;
-}
-
void FakePointerController::move(float deltaX, float deltaY) {
if (!mEnabled) return;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 2c76c62..9b773a7 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -65,7 +65,6 @@
private:
std::string dump() override { return ""; }
- std::optional<FloatRect> getBounds() const override;
void move(float deltaX, float deltaY) override;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index d0cd677..225ae0f 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -279,6 +279,8 @@
}
TEST_F(GestureConverterTest, Scroll) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
const nsecs_t downTime = 12345;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
@@ -300,7 +302,8 @@
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
WithToolType(ToolType::FINGER),
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
@@ -312,7 +315,8 @@
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
@@ -325,7 +329,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
@@ -845,6 +850,8 @@
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -867,7 +874,8 @@
AllOf(WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -879,7 +887,8 @@
WithGesturePinchScaleFactor(0.8f, EPSILON),
WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0),
WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -891,12 +900,14 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
+ WithPointerCount(2u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
+ WithPointerCount(1u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
@@ -908,6 +919,8 @@
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -930,7 +943,8 @@
AllOf(WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 1.1, GESTURES_ZOOM_UPDATE);
@@ -942,7 +956,8 @@
WithGesturePinchScaleFactor(1.1f, EPSILON),
WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0),
WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -954,12 +969,14 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
+ WithPointerCount(2u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
+ WithPointerCount(1u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
@@ -1055,6 +1072,8 @@
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -1070,7 +1089,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp
index 8dfa8c8..e87f822 100644
--- a/services/inputflinger/tests/HardwareProperties_test.cpp
+++ b/services/inputflinger/tests/HardwareProperties_test.cpp
@@ -48,24 +48,19 @@
static constexpr int32_t EVENTHUB_ID = 1;
void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
- .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
- outAxisInfo->valid = true;
- outAxisInfo->minValue = min;
- outAxisInfo->maxValue = max;
- outAxisInfo->flat = 0;
- outAxisInfo->fuzz = 0;
- outAxisInfo->resolution = resolution;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
+ .WillRepeatedly(Return(std::optional<RawAbsoluteAxisInfo>{{
+ .minValue = min,
+ .maxValue = max,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = resolution,
+ }}));
}
void setupInvalidAxis(int axis) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
- .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
- outAxisInfo->valid = false;
- return -1;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
+ .WillRepeatedly(Return(std::nullopt));
}
void setProperty(int property, bool value) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 48930ef..7b5c47b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -411,16 +411,6 @@
<< "Should reject motion events with duplicate pointer ids.";
}
-/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
-
-TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
- constexpr nsecs_t eventTime = 20;
- mDispatcher->notifyConfigurationChanged({/*id=*/10, eventTime});
- ASSERT_TRUE(mDispatcher->waitForIdle());
-
- mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
-}
-
TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0,
/*switchValues=*/1,
@@ -4265,6 +4255,123 @@
window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
}
+/**
+ * When events are not split, the downTime should be adjusted such that the downTime corresponds
+ * to the event time of the first ACTION_DOWN. If a new window appears, it should not affect
+ * the event routing because the first window prevents splitting.
+ */
+TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window1 =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
+ window1->setTouchableRegion(Region{{0, 0, 100, 100}});
+ window1->setPreventSplitting(true);
+
+ sp<FakeWindowHandle> window2 =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
+ window2->setTouchableRegion(Region{{100, 0, 200, 100}});
+
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo()}, {}, 0, 0});
+
+ // Touch down on the first window
+ NotifyMotionArgs downArgs = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build();
+ mDispatcher->notifyMotion(downArgs);
+
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime)));
+
+ // Second window is added
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
+
+ // Now touch down on the window with another pointer
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .downTime(downArgs.downTime)
+ .build());
+ window1->consumeMotionPointerDown(1, AllOf(WithDownTime(downArgs.downTime)));
+
+ // Finish the gesture
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .downTime(downArgs.downTime)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .downTime(downArgs.downTime)
+ .build());
+ window1->consumeMotionPointerUp(1, AllOf(WithDownTime(downArgs.downTime)));
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime)));
+ window2->assertNoEvents();
+}
+
+/**
+ * When splitting touch events, the downTime should be adjusted such that the downTime corresponds
+ * to the event time of the first ACTION_DOWN sent to the new window.
+ * If a new window that does not support split appears on the screen and gets touched with the
+ * second finger, it should not get any events because it doesn't want split touches. At the same
+ * time, the first window should not get the pointer_down event because it supports split touches
+ * (and the touch occurred outside of the bounds of window1).
+ */
+TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window1 =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
+ window1->setTouchableRegion(Region{{0, 0, 100, 100}});
+
+ sp<FakeWindowHandle> window2 =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
+ window2->setTouchableRegion(Region{{100, 0, 200, 100}});
+
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo()}, {}, 0, 0});
+
+ // Touch down on the first window
+ NotifyMotionArgs downArgs = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build();
+ mDispatcher->notifyMotion(downArgs);
+
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime)));
+
+ // Second window is added
+ window2->setPreventSplitting(true);
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
+
+ // Now touch down on the window with another pointer
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .downTime(downArgs.downTime)
+ .build());
+ // Event is dropped because window2 doesn't support split touch, and window1 does.
+
+ // Complete the gesture
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .downTime(downArgs.downTime)
+ .build());
+ // A redundant MOVE event is generated that doesn't carry any new information
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime)));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .downTime(downArgs.downTime)
+ .build());
+
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime)));
+ window1->assertNoEvents();
+ window2->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
@@ -4469,6 +4576,202 @@
window->assertNoEvents();
}
+/**
+ * A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even
+ * though the window underneath should not get any events.
+ */
+TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowSinglePointer) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 100, 100));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setPreventSplitting(true);
+ spyWindow->setSpy(true);
+ // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ sp<FakeWindowHandle> inputSinkWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
+ ui::LogicalDisplayId::DEFAULT);
+ inputSinkWindow->setFrame(Rect(0, 0, 100, 100));
+ inputSinkWindow->setTrustedOverlay(true);
+ inputSinkWindow->setPreventSplitting(true);
+ inputSinkWindow->setNoInputChannel(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *inputSinkWindow->getInfo()}, {}, 0, 0});
+
+ // Tap the spy window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(51))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51))
+ .build());
+
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP)));
+ inputSinkWindow->assertNoEvents();
+}
+
+/**
+ * A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even
+ * though the window underneath should not get any events.
+ * Same test as above, but with two pointers touching instead of one.
+ */
+TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowTwoPointers) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 100, 100));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setPreventSplitting(true);
+ spyWindow->setSpy(true);
+ // Another window below spy that would have both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ sp<FakeWindowHandle> inputSinkWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
+ ui::LogicalDisplayId::DEFAULT);
+ inputSinkWindow->setFrame(Rect(0, 0, 100, 100));
+ inputSinkWindow->setTrustedOverlay(true);
+ inputSinkWindow->setPreventSplitting(true);
+ inputSinkWindow->setNoInputChannel(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *inputSinkWindow->getInfo()}, {}, 0, 0});
+
+ // Both fingers land into the spy window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(51))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(11))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(11))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(51))
+ .build());
+
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+ spyWindow->consumeMotionPointerDown(1, WithPointerCount(2));
+ spyWindow->consumeMotionPointerUp(1, WithPointerCount(2));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP)));
+ inputSinkWindow->assertNoEvents();
+}
+
+/** Check the behaviour for cases where input sink prevents or doesn't prevent splitting. */
+class SpyThatPreventsSplittingWithApplicationFixture : public InputDispatcherTest,
+ public ::testing::WithParamInterface<bool> {
+};
+
+/**
+ * Three windows:
+ * - An application window (app window)
+ * - A spy window that does not overlap the app window. Has PREVENT_SPLITTING flag
+ * - A window below the spy that has NO_INPUT_CHANNEL (call it 'inputSink')
+ *
+ * The spy window is side-by-side with the app window. The inputSink is below the spy.
+ * We first touch the area outside of the appWindow, but inside spyWindow.
+ * Only the SPY window should get the DOWN event.
+ * The spy pilfers after receiving the first DOWN event.
+ * Next, we touch the app window.
+ * The spy should receive POINTER_DOWN(1) (since spy is preventing splits).
+ * Also, since the spy is already pilfering the first pointer, it will be sent the remaining new
+ * pointers automatically, as well.
+ * Next, the first pointer (from the spy) is lifted.
+ * Spy should get POINTER_UP(0).
+ * This event should not go to the app because the app never received this pointer to begin with.
+ * Now, lift the remaining pointer and check that the spy receives UP event.
+ *
+ * Finally, send a new ACTION_DOWN event to the spy and check that it's received.
+ * This test attempts to reproduce a crash in the dispatcher.
+ */
+TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingWithApplication) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(100, 100, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setPreventSplitting(true);
+ spyWindow->setSpy(true);
+ // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ sp<FakeWindowHandle> inputSinkWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
+ ui::LogicalDisplayId::DEFAULT);
+ inputSinkWindow->setFrame(Rect(100, 100, 200, 200)); // directly below the spy
+ inputSinkWindow->setTrustedOverlay(true);
+ inputSinkWindow->setPreventSplitting(GetParam());
+ inputSinkWindow->setNoInputChannel(true);
+
+ sp<FakeWindowHandle> appWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "App",
+ ui::LogicalDisplayId::DEFAULT);
+ appWindow->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *inputSinkWindow->getInfo(), *appWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ // First finger lands outside of the appWindow, but inside of the spy window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
+ .build());
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+
+ mDispatcher->pilferPointers(spyWindow->getToken());
+
+ // Second finger lands in the app, and goes to the spy window. It doesn't go to the app because
+ // the spy is already pilfering the first pointer, and this automatically grants the remaining
+ // new pointers to the spy, as well.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+ .build());
+
+ spyWindow->consumeMotionPointerDown(1, WithPointerCount(2));
+
+ // Now lift up the first pointer
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+ .build());
+ spyWindow->consumeMotionPointerUp(0, WithPointerCount(2));
+
+ // And lift the remaining pointer!
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+ .build());
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1)));
+
+ // Now send a new DOWN, which should again go to spy.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
+ .build());
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+ // The app window doesn't get any events this entire time because the spy received the events
+ // first and pilfered, which makes all new pointers go to it as well.
+ appWindow->assertNoEvents();
+}
+
+// Behaviour should be the same regardless of whether inputSink supports splitting.
+INSTANTIATE_TEST_SUITE_P(SpyThatPreventsSplittingWithApplication,
+ SpyThatPreventsSplittingWithApplicationFixture, testing::Bool());
+
TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -4827,6 +5130,54 @@
}
/**
+ * Invalid events injected by input filter are rejected.
+ */
+TEST_F(InputDispatcherTest, InvalidA11yEventsGetRejected) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // a11y sets 'POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY' policy flag during injection, so define
+ // a custom injection function here for convenience.
+ auto injectFromAccessibility = [&](int32_t action, float x, float y) {
+ MotionEvent event = MotionEventBuilder(action, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(x).y(y))
+ .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT)
+ .build();
+ return injectMotionEvent(*mDispatcher, event, 100ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, /*targetUid=*/{},
+ POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_FILTERED |
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY);
+ };
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/300, /*y=*/400));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_MOVE, /*x=*/310, /*y=*/420));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ // finger is still down, so a new DOWN event should be rejected!
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/340, /*y=*/410));
+
+ // if the gesture is correctly finished, new down event will succeed
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_MOVE, /*x=*/320, /*y=*/430));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_UP, /*x=*/320, /*y=*/430));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/350, /*y=*/460));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
* If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
*/
TEST_F(InputDispatcherTest, TouchDownAfterMouseHover_legacy) {
@@ -5376,6 +5727,7 @@
}
TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
@@ -5421,6 +5773,7 @@
* "incomplete" gestures.
*/
TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
@@ -5451,6 +5804,273 @@
}
/**
+ * Three windows:
+ * 1) A window on the left, with flag dup_to_wallpaper
+ * 2) A window on the right, with flag slippery
+ * 3) A wallpaper window under the left window
+ * When touch slips from right window to left, the wallpaper should receive a similar slippery
+ * enter event. Later on, when another device becomes active, the wallpaper should receive
+ * consistent streams from the new device, and also from the old device.
+ * This test attempts to reproduce a crash in the dispatcher where the wallpaper target's downTime
+ * was not getting set during slippery entrance.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ auto firstFingerMoveFromDeviceA = AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE),
+ WithPointerCount(1), WithPointerId(0, 0));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Same test as above, but with enable_multi_device_same_window_stream flag set to false.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ // This device was already canceled, so MOVE events will not be arriving to the windows from it.
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->assertNoEvents();
+}
+
+/**
* Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
* down event to the right window. Device B sends a down event to the left window, and then a
* POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
@@ -5458,6 +6078,7 @@
* This test attempts to reproduce a crash.
*/
TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)",
@@ -8204,6 +8825,7 @@
* the previous window should receive this event and not be dropped.
*/
TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
ui::LogicalDisplayId::DEFAULT);
@@ -8562,6 +9184,7 @@
protected:
static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms;
+ static constexpr bool KEY_REPEAT_ENABLED = true;
std::shared_ptr<FakeApplicationHandle> mApp;
sp<FakeWindowHandle> mWindow;
@@ -8569,7 +9192,8 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY,
+ KEY_REPEAT_ENABLED);
setUpWindow();
}
@@ -8718,6 +9342,24 @@
expectKeyRepeatOnce(3);
}
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_NoRepeatWhenKeyRepeatDisabled) {
+ SCOPED_FLAG_OVERRIDE(keyboard_repeat_keys, true);
+ static constexpr std::chrono::milliseconds KEY_NO_REPEAT_ASSERTION_TIMEOUT = 100ms;
+
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY,
+ /*repeatKeyEnabled=*/false);
+ sendAndConsumeKeyDown(/*deviceId=*/1);
+
+ ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_TIMEOUT)
+ << "Ensure the check for no key repeats extends beyond the repeat timeout duration.";
+ ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_DELAY)
+ << "Ensure the check for no key repeats extends beyond the repeat delay duration.";
+
+ // No events should be returned if key repeat is turned off.
+ // Wait for KEY_NO_REPEAT_ASSERTION_TIMEOUT to return no events to ensure key repeat disabled.
+ mWindow->assertNoEvents(KEY_NO_REPEAT_ASSERTION_TIMEOUT);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index ff0de83..7dff144 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -54,16 +54,15 @@
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
int32_t resolution) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, _))
- .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
- outAxisInfo->valid = valid;
- outAxisInfo->minValue = min;
- outAxisInfo->maxValue = max;
- outAxisInfo->flat = 0;
- outAxisInfo->fuzz = 0;
- outAxisInfo->resolution = resolution;
- return valid ? OK : -1;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
+ .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{
+ .minValue = min,
+ .maxValue = max,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = resolution,
+ }}
+ : std::nullopt));
}
void InputMapperUnitTest::expectScanCodes(bool present, std::set<int> scanCodes) {
@@ -87,6 +86,13 @@
}
}
+void InputMapperUnitTest::setSwitchState(int32_t state, std::set<int32_t> switchCodes) {
+ for (const auto& switchCode : switchCodes) {
+ EXPECT_CALL(mMockEventHub, getSwitchState(EVENTHUB_ID, switchCode))
+ .WillRepeatedly(testing::Return(static_cast<int>(state)));
+ }
+}
+
std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) {
nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
return process(when, type, code, value);
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 4271a70..fc27e4f 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -51,6 +51,8 @@
void setKeyCodeState(KeyState state, std::set<int> keyCodes);
+ void setSwitchState(int32_t state, std::set<int32_t> switchCodes);
+
std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
diff --git a/services/inputflinger/tests/InputProcessor_test.cpp b/services/inputflinger/tests/InputProcessor_test.cpp
index f7e5e67..d4c5a00 100644
--- a/services/inputflinger/tests/InputProcessor_test.cpp
+++ b/services/inputflinger/tests/InputProcessor_test.cpp
@@ -63,20 +63,6 @@
void SetUp() override { mProcessor = std::make_unique<InputProcessor>(mTestListener); }
};
-/**
- * Create a basic configuration change and send it to input processor.
- * Expect that the event is received by the next input stage, unmodified.
- */
-TEST_F(InputProcessorTest, SendToNextStage_NotifyConfigurationChangedArgs) {
- // Create a basic configuration change and send to processor
- NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2);
-
- mProcessor->notifyConfigurationChanged(args);
- NotifyConfigurationChangedArgs outArgs;
- ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
- ASSERT_EQ(args, outArgs);
-}
-
TEST_F(InputProcessorTest, SendToNextStage_NotifyKeyArgs) {
// Create a basic key event and send to processor
NotifyKeyArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*readTime=*/21, /*deviceId=*/3,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 93fae9b..17c37d5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -24,19 +24,16 @@
#include <InputReader.h>
#include <InputReaderBase.h>
#include <InputReaderFactory.h>
-#include <JoystickInputMapper.h>
#include <KeyboardInputMapper.h>
#include <MultiTouchInputMapper.h>
#include <NotifyArgsBuilders.h>
#include <PeripheralController.h>
#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
-#include <SwitchInputMapper.h>
#include <TestEventMatchers.h>
#include <TestInputListener.h>
#include <TouchInputMapper.h>
#include <UinputDevice.h>
-#include <VibratorInputMapper.h>
#include <android-base/thread_annotations.h>
#include <com_android_input_flags.h>
#include <ftl/enum.h>
@@ -624,7 +621,6 @@
if (configuration) {
mFakeEventHub->addConfigurationMap(eventHubId, configuration);
}
- mFakeEventHub->finishDeviceScan();
mReader->loopOnce();
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -758,8 +754,6 @@
mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
-
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(deviceId, resetArgs.deviceId);
@@ -775,7 +769,6 @@
disableDevice(deviceId);
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
ASSERT_EQ(device->isEnabled(), false);
enableDevice(deviceId);
@@ -960,16 +953,6 @@
ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
}
-TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
- constexpr int32_t eventHubId = 1;
- addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr);
-
- NotifyConfigurationChangedArgs args;
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
-}
-
TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
constexpr ftl::Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
@@ -1074,7 +1057,6 @@
// The device is added after the input port associations are processed since
// we do not yet support dynamic device-to-display associations.
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
@@ -1104,8 +1086,6 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
-
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(deviceId, resetArgs.deviceId);
@@ -1477,9 +1457,8 @@
// Since this test is run on a real device, all the input devices connected
// to the test device will show up in mReader. We wait for those input devices to
// show up before beginning the tests.
- ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
}
};
@@ -1499,12 +1478,10 @@
// consider it as a valid device.
std::unique_ptr<UinputDevice> invalidDevice = createUinputDevice<InvalidUinputDevice>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled());
ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
invalidDevice.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled());
ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
}
@@ -1513,7 +1490,6 @@
std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
const auto device = waitForDevice(keyboard->getName());
@@ -1524,7 +1500,6 @@
keyboard.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
ASSERT_EQ(initialNumDevices, mFakePolicy->getInputDevices().size());
}
@@ -1532,21 +1507,14 @@
std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- NotifyConfigurationChangedArgs configChangedArgs;
- ASSERT_NO_FATAL_FAILURE(
- mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs));
- int32_t prevId = configChangedArgs.id;
- nsecs_t prevTimestamp = configChangedArgs.eventTime;
-
NotifyKeyArgs keyArgs;
keyboard->pressAndReleaseHomeKey();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_NE(prevId, keyArgs.id);
- prevId = keyArgs.id;
- ASSERT_LE(prevTimestamp, keyArgs.eventTime);
ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
- prevTimestamp = keyArgs.eventTime;
+
+ int32_t prevId = keyArgs.id;
+ nsecs_t prevTimestamp = keyArgs.eventTime;
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
@@ -1669,7 +1637,6 @@
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto info = waitForDevice(mDevice->getName());
ASSERT_TRUE(info);
mDeviceInfo = *info;
@@ -1738,7 +1705,6 @@
UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT,
ViewportType::INTERNAL);
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto info = waitForDevice(mDevice->getName());
ASSERT_TRUE(info);
mDeviceInfo = *info;
@@ -2071,7 +2037,6 @@
// Connecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
auto externalStylus = createUinputDevice<UinputExternalStylus>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto stylusInfo = waitForDevice(externalStylus->getName());
ASSERT_TRUE(stylusInfo);
@@ -2084,7 +2049,6 @@
// Disconnecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
externalStylus.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
// Up
@@ -2142,7 +2106,6 @@
mStylusDeviceLifecycleTracker = createUinputDevice<T>();
mStylus = mStylusDeviceLifecycleTracker.get();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto info = waitForDevice(mStylus->getName());
ASSERT_TRUE(info);
mStylusInfo = *info;
@@ -2412,7 +2375,6 @@
std::unique_ptr<UinputExternalStylusWithPressure> stylus =
createUinputDevice<UinputExternalStylusWithPressure>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto stylusInfo = waitForDevice(stylus->getName());
ASSERT_TRUE(stylusInfo);
@@ -2430,7 +2392,6 @@
std::unique_ptr<UinputExternalStylusWithPressure> stylus =
createUinputDevice<UinputExternalStylusWithPressure>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto stylusInfo = waitForDevice(stylus->getName());
ASSERT_TRUE(stylusInfo);
@@ -2476,7 +2437,6 @@
std::unique_ptr<UinputExternalStylusWithPressure> stylus =
createUinputDevice<UinputExternalStylusWithPressure>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto stylusInfo = waitForDevice(stylus->getName());
ASSERT_TRUE(stylusInfo);
@@ -2556,7 +2516,6 @@
// touch pointers.
std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
const auto stylusInfo = waitForDevice(stylus->getName());
ASSERT_TRUE(stylusInfo);
@@ -3058,106 +3017,6 @@
mapper.assertProcessWasCalled();
}
-// --- SwitchInputMapperTest ---
-
-class SwitchInputMapperTest : public InputMapperTest {
-protected:
-};
-
-TEST_F(SwitchInputMapperTest, GetSources) {
- SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
-
- ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
-}
-
-TEST_F(SwitchInputMapperTest, GetSwitchState) {
- SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
-
- mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1);
- ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
-
- mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0);
- ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
-}
-
-TEST_F(SwitchInputMapperTest, Process) {
- SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
- std::list<NotifyArgs> out;
- out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
- ASSERT_TRUE(out.empty());
- out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
- ASSERT_TRUE(out.empty());
- out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
- ASSERT_TRUE(out.empty());
- out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
- ASSERT_EQ(1u, out.size());
- const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin());
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
- ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
- args.switchMask);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-}
-
-// --- VibratorInputMapperTest ---
-class VibratorInputMapperTest : public InputMapperTest {
-protected:
- void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::VIBRATOR); }
-};
-
-TEST_F(VibratorInputMapperTest, GetSources) {
- VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(VibratorInputMapperTest, GetVibratorIds) {
- VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
-
- ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
-}
-
-TEST_F(VibratorInputMapperTest, Vibrate) {
- constexpr uint8_t DEFAULT_AMPLITUDE = 192;
- constexpr int32_t VIBRATION_TOKEN = 100;
- VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
-
- VibrationElement pattern(2);
- VibrationSequence sequence(2);
- pattern.duration = std::chrono::milliseconds(200);
- pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 2},
- {/*vibratorId=*/1, DEFAULT_AMPLITUDE}};
- sequence.addElement(pattern);
- pattern.duration = std::chrono::milliseconds(500);
- pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 4},
- {/*vibratorId=*/1, DEFAULT_AMPLITUDE}};
- sequence.addElement(pattern);
-
- std::vector<int64_t> timings = {0, 1};
- std::vector<uint8_t> amplitudes = {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE / 2};
-
- ASSERT_FALSE(mapper.isVibrating());
- // Start vibrating
- std::list<NotifyArgs> out = mapper.vibrate(sequence, /*repeat=*/-1, VIBRATION_TOKEN);
- ASSERT_TRUE(mapper.isVibrating());
- // Verify vibrator state listener was notified.
- mReader->loopOnce();
- ASSERT_EQ(1u, out.size());
- const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
- ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId);
- ASSERT_TRUE(vibrateArgs.isOn);
- // Stop vibrating
- out = mapper.cancelVibrate(VIBRATION_TOKEN);
- ASSERT_FALSE(mapper.isVibrating());
- // Verify vibrator state listener was notified.
- mReader->loopOnce();
- ASSERT_EQ(1u, out.size());
- const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
- ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId);
- ASSERT_FALSE(cancelArgs.isOn);
-}
-
// --- SensorInputMapperTest ---
class SensorInputMapperTest : public InputMapperTest {
@@ -3471,11 +3330,11 @@
TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
- mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
KeyboardInputMapper& mapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}});
// Key down by scan code.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
NotifyKeyArgs args;
@@ -6337,7 +6196,7 @@
SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
- ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) {
@@ -6360,7 +6219,7 @@
InputReaderConfiguration::Change::DEVICE_TYPE /*changes*/);
// Check whether device type update was successful.
- ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION | AINPUT_SOURCE_TOUCHPAD, mDevice->getSources());
}
TEST_F(SingleTouchInputMapperTest, HoverEventsOutsidePhysicalFrameAreIgnored) {
@@ -6816,15 +6675,27 @@
class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
public:
- SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
+ void SetUp() override {
+ SingleTouchInputMapperTest::SetUp();
+ mExternalStylusDeviceInfo = {};
+ mStylusState = {};
+ }
+
+ SingleTouchInputMapper& initializeInputMapperWithExternalStylus(bool supportsPressure = true) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
+ if (supportsPressure) {
+ mExternalStylusDeviceInfo.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
+ AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
+ mStylusState.pressure = 0.f;
+ }
+
mStylusState.when = ARBITRARY_TIME;
- mStylusState.pressure = 0.f;
mStylusState.toolType = ToolType::STYLUS;
mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
@@ -6932,11 +6803,17 @@
InputDeviceInfo mExternalStylusDeviceInfo{};
};
-TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
+TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSourceWithPressure) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
ASSERT_EQ(STYLUS_FUSION_SOURCE, mapper.getSources());
}
+TEST_F(ExternalStylusFusionTest, DoesNotUseBluetoothStylusSourceWithoutPressure) {
+ SingleTouchInputMapper& mapper =
+ initializeInputMapperWithExternalStylus(/*supportsPressure=*/false);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+}
+
TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
@@ -10203,67 +10080,6 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-// --- JoystickInputMapperTest ---
-
-class JoystickInputMapperTest : public InputMapperTest {
-protected:
- static const int32_t RAW_X_MIN;
- static const int32_t RAW_X_MAX;
- static const int32_t RAW_Y_MIN;
- static const int32_t RAW_Y_MAX;
-
- void SetUp() override {
- InputMapperTest::SetUp(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL);
- }
- void prepareAxes() {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
- }
-
- void processAxis(JoystickInputMapper& mapper, int32_t axis, int32_t value) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, axis, value);
- }
-
- void processSync(JoystickInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- }
-
- void prepareVirtualDisplay(ui::Rotation orientation) {
- setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
- VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
- NO_PORT, ViewportType::VIRTUAL);
- }
-};
-
-const int32_t JoystickInputMapperTest::RAW_X_MIN = -32767;
-const int32_t JoystickInputMapperTest::RAW_X_MAX = 32767;
-const int32_t JoystickInputMapperTest::RAW_Y_MIN = -32767;
-const int32_t JoystickInputMapperTest::RAW_Y_MAX = 32767;
-
-TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
- prepareAxes();
- JoystickInputMapper& mapper = constructAndAddMapper<JoystickInputMapper>();
-
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
-
- prepareVirtualDisplay(ui::ROTATION_0);
-
- // Send an axis event
- processAxis(mapper, ABS_X, 100);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
-
- // Send another axis event
- processAxis(mapper, ABS_Y, 100);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
-}
-
// --- PeripheralControllerTest ---
class PeripheralControllerTest : public testing::Test {
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 8a15d07..f41b39a 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -92,12 +92,13 @@
MOCK_METHOD(InputDeviceIdentifier, getDeviceIdentifier, (int32_t deviceId), (const));
MOCK_METHOD(int32_t, getDeviceControllerNumber, (int32_t deviceId), (const));
MOCK_METHOD(std::optional<PropertyMap>, getConfiguration, (int32_t deviceId), (const));
- MOCK_METHOD(status_t, getAbsoluteAxisInfo,
- (int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo), (const));
+ MOCK_METHOD(std::optional<RawAbsoluteAxisInfo>, getAbsoluteAxisInfo,
+ (int32_t deviceId, int axis), (const));
MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const));
MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const));
MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const));
- MOCK_METHOD(void, addKeyRemapping, (int32_t deviceId, int fromKeyCode, int toKeyCode), (const));
+ MOCK_METHOD(void, setKeyRemapping,
+ (int32_t deviceId, (const std::map<int32_t, int32_t>& keyRemapping)), (const));
MOCK_METHOD(status_t, mapKey,
(int32_t deviceId, int scanCode, int usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags),
@@ -132,7 +133,7 @@
MOCK_METHOD(int32_t, getKeyCodeState, (int32_t deviceId, int32_t keyCode), (const, override));
MOCK_METHOD(int32_t, getSwitchState, (int32_t deviceId, int32_t sw), (const, override));
- MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue),
+ MOCK_METHOD(std::optional<int32_t>, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis),
(const, override));
MOCK_METHOD(base::Result<std::vector<int32_t>>, getMtSlotValues,
(int32_t deviceId, int32_t axis, size_t slotCount), (const, override));
@@ -188,6 +189,7 @@
MOCK_METHOD(void, notifyPointerDisplayIdChanged,
(ui::LogicalDisplayId displayId, const FloatPoint& position), (override));
MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
+ MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
};
class MockInputDevice : public InputDevice {
@@ -197,6 +199,7 @@
: InputDevice(context, id, generation, identifier) {}
MOCK_METHOD(uint32_t, getSources, (), (const, override));
+ MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const));
MOCK_METHOD(bool, isEnabled, (), ());
MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
@@ -245,8 +248,6 @@
MOCK_METHOD(int32_t, getMetaState, (), ());
MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ());
- MOCK_METHOD(void, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ());
-
MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
MOCK_METHOD(void, bumpGeneration, (), ());
diff --git a/services/inputflinger/tests/JoystickInputMapper_test.cpp b/services/inputflinger/tests/JoystickInputMapper_test.cpp
new file mode 100644
index 0000000..adebd72
--- /dev/null
+++ b/services/inputflinger/tests/JoystickInputMapper_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#include "JoystickInputMapper.h"
+
+#include <list>
+#include <optional>
+
+#include <EventHub.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
+#include <linux/input-event-codes.h>
+#include <ui/LogicalDisplayId.h>
+
+#include "InputMapperTest.h"
+#include "TestConstants.h"
+#include "TestEventMatchers.h"
+
+namespace android {
+
+using namespace ftl::flag_operators;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::Return;
+using testing::VariantWith;
+
+class JoystickInputMapperTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL));
+
+ // The mapper requests info on all ABS axis IDs, including ones which aren't actually used
+ // (e.g. in the range from 0x0b (ABS_BRAKE) to 0x0f (ABS_HAT0X)), so just return nullopt for
+ // all axes we don't explicitly set up below.
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_))
+ .WillRepeatedly(Return(std::nullopt));
+
+ setupAxis(ABS_X, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0);
+ setupAxis(ABS_Y, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0);
+ }
+};
+
+TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
+ DisplayViewport viewport;
+ viewport.displayId = ui::LogicalDisplayId{1};
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(viewport));
+ mMapper = createInputMapper<JoystickInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+
+ std::list<NotifyArgs> out;
+
+ // Send an axis event
+ out = process(EV_ABS, ABS_X, 100);
+ ASSERT_THAT(out, IsEmpty());
+ out = process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId))));
+
+ // Send another axis event
+ out = process(EV_ABS, ABS_Y, 100);
+ ASSERT_THAT(out, IsEmpty());
+ out = process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId))));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 4d322e9..88c25d3 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -70,48 +70,8 @@
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
}
-
- void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
- const bool expectPrevent) {
- if (expectPrevent) {
- EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
- .Times(keyCodes.size());
- }
- for (int32_t keyCode : keyCodes) {
- process(EV_KEY, keyCode, 1);
- process(EV_SYN, SYN_REPORT, 0);
- process(EV_KEY, keyCode, 0);
- process(EV_SYN, SYN_REPORT, 0);
- }
- }
};
-/**
- * Touchpad tap should not be disabled if there is no active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
- testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false);
-}
-
-/**
- * Touchpad tap should be disabled if there is a active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true);
-}
-
-/**
- * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active
- */
-TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
- KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
- KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
- testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
-}
-
TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) {
nsecs_t when = ARBITRARY_TIME;
std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT};
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index 4fcffdd..0f92833 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -61,12 +61,12 @@
InputEventTimeline getTestTimeline() {
InputEventTimeline t(
- /*isDown=*/true,
/*eventTime=*/2,
/*readTime=*/3,
/*vendorId=*/0,
/*productId=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8);
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
@@ -116,9 +116,10 @@
void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) {
const nsecs_t triggerEventTime =
lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
- mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/true, triggerEventTime,
+ mTracker->trackListener(/*inputEventId=*/1, triggerEventTime,
/*readTime=*/3, DEVICE_ID,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
}
void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
@@ -167,12 +168,15 @@
* any additional ConnectionTimeline's.
*/
TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
- mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/false, /*eventTime=*/2,
- /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(/*inputEventId=*/1, /*eventTime=*/2,
+ /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN},
+ AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
triggerEventReporting(/*eventTime=*/2);
- assertReceivedTimeline(InputEventTimeline{/*isDown=*/false, /*eventTime=*/2,
- /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN}});
+ assertReceivedTimeline(
+ InputEventTimeline{/*eventTime=*/2,
+ /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT});
}
/**
@@ -203,8 +207,9 @@
const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
- mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
- DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
+ InputEventType::MOTION);
mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
@@ -220,14 +225,15 @@
TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) {
constexpr nsecs_t inputEventId = 1;
constexpr nsecs_t readTime = 3; // does not matter for this test
- constexpr bool isDown = true; // does not matter for this test
// In the following 2 calls to trackListener, the inputEventId's are the same, but event times
// are different.
- mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN});
- mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime, DEVICE_ID,
- {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId, /*eventTime=*/1, readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
+ InputEventType::MOTION);
+ mTracker->trackListener(inputEventId, /*eventTime=*/2, readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
+ InputEventType::MOTION);
triggerEventReporting(/*eventTime=*/2);
// Since we sent duplicate input events, the tracker should just delete all of them, because it
@@ -238,12 +244,12 @@
TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) {
constexpr int32_t inputEventId1 = 1;
InputEventTimeline timeline1(
- /*isDown*/ true,
/*eventTime*/ 2,
/*readTime*/ 3,
/*vendorId=*/0,
/*productId=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*inputEventType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
timeline1.connectionTimelines.emplace(connection1,
ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
/*finishTime*/ 8));
@@ -255,12 +261,12 @@
constexpr int32_t inputEventId2 = 10;
InputEventTimeline timeline2(
- /*isDown=*/false,
/*eventTime=*/20,
/*readTime=*/30,
/*vendorId=*/0,
/*productId=*/0,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
timeline2.connectionTimelines.emplace(connection2,
ConnectionTimeline(/*deliveryTime=*/60,
/*consumeTime=*/70,
@@ -272,11 +278,13 @@
connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2));
// Start processing first event
- mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime,
- timeline1.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId1, timeline1.eventTime, timeline1.readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
+ InputEventType::MOTION);
// Start processing second event
- mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime,
- timeline2.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId2, timeline2.eventTime, timeline2.readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
+ InputEventType::MOTION);
mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
@@ -301,12 +309,14 @@
const sp<IBinder>& token = timeline.connectionTimelines.begin()->first;
for (size_t i = 1; i <= 100; i++) {
- mTracker->trackListener(/*inputEventId=*/i, timeline.isDown, timeline.eventTime,
- timeline.readTime, /*deviceId=*/DEVICE_ID,
- /*sources=*/{InputDeviceUsageSource::UNKNOWN});
- expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime,
- timeline.readTime, timeline.vendorId,
- timeline.productId, timeline.sources});
+ mTracker->trackListener(/*inputEventId=*/i, timeline.eventTime, timeline.readTime,
+ /*deviceId=*/DEVICE_ID,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN},
+ AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
+ expectedTimelines.push_back(InputEventTimeline{timeline.eventTime, timeline.readTime,
+ timeline.vendorId, timeline.productId,
+ timeline.sources,
+ timeline.inputEventActionType});
}
// Now, complete the first event that was sent.
mTracker->trackFinishedEvent(/*inputEventId=*/1, token, expectedCT.deliveryTime,
@@ -332,12 +342,13 @@
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
- mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
- DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
+ InputEventType::MOTION);
triggerEventReporting(expected.eventTime);
- assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime,
- expected.readTime, expected.vendorId,
- expected.productId, expected.sources});
+ assertReceivedTimeline(InputEventTimeline{expected.eventTime, expected.readTime,
+ expected.vendorId, expected.productId,
+ expected.sources, expected.inputEventActionType});
}
/**
@@ -348,22 +359,92 @@
TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) {
constexpr int32_t inputEventId = 1;
InputEventTimeline timeline(
- /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3,
+ /*eventTime*/ 2, /*readTime*/ 3,
/*vendorId=*/50, /*productId=*/60,
/*sources=*/
- {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT});
+ {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT},
+ /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT);
InputDeviceInfo deviceInfo1 = generateTestDeviceInfo(
/*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1);
InputDeviceInfo deviceInfo2 = generateTestDeviceInfo(
/*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID);
mTracker->setInputDevices({deviceInfo1, deviceInfo2});
- mTracker->trackListener(inputEventId, timeline.isDown, timeline.eventTime, timeline.readTime,
- DEVICE_ID,
+ mTracker->trackListener(inputEventId, timeline.eventTime, timeline.readTime, DEVICE_ID,
{InputDeviceUsageSource::TOUCHSCREEN,
- InputDeviceUsageSource::STYLUS_DIRECT});
+ InputDeviceUsageSource::STYLUS_DIRECT},
+ AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
triggerEventReporting(timeline.eventTime);
assertReceivedTimeline(timeline);
}
+/**
+ * Check that InputEventActionType is correctly assigned to InputEventTimeline in trackListener.
+ */
+TEST_F(LatencyTrackerTest, TrackListenerCheck_InputEventActionTypeFieldInputEventTimeline) {
+ constexpr int32_t inputEventId = 1;
+ // Create timelines for different event types (Motion, Key)
+ InputEventTimeline motionDownTimeline(
+ /*eventTime*/ 2, /*readTime*/ 3,
+ /*vendorId*/ 0, /*productId*/ 0,
+ /*sources*/ {InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_DOWN);
+
+ InputEventTimeline motionMoveTimeline(
+ /*eventTime*/ 4, /*readTime*/ 5,
+ /*vendorId*/ 0, /*productId*/ 0,
+ /*sources*/ {InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_MOVE);
+
+ InputEventTimeline motionUpTimeline(
+ /*eventTime*/ 6, /*readTime*/ 7,
+ /*vendorId*/ 0, /*productId*/ 0,
+ /*sources*/ {InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_UP);
+
+ InputEventTimeline keyDownTimeline(
+ /*eventTime*/ 8, /*readTime*/ 9,
+ /*vendorId*/ 0, /*productId*/ 0,
+ /*sources*/ {InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType*/ InputEventActionType::KEY);
+
+ InputEventTimeline keyUpTimeline(
+ /*eventTime*/ 10, /*readTime*/ 11,
+ /*vendorId*/ 0, /*productId*/ 0,
+ /*sources*/ {InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType*/ InputEventActionType::KEY);
+
+ InputEventTimeline unknownTimeline(
+ /*eventTime*/ 12, /*readTime*/ 13,
+ /*vendorId*/ 0, /*productId*/ 0,
+ /*sources*/ {InputDeviceUsageSource::UNKNOWN},
+ /*inputEventActionType*/ InputEventActionType::UNKNOWN_INPUT_EVENT);
+
+ mTracker->trackListener(inputEventId, motionDownTimeline.eventTime, motionDownTimeline.readTime,
+ DEVICE_ID, motionDownTimeline.sources, AMOTION_EVENT_ACTION_DOWN,
+ InputEventType::MOTION);
+ mTracker->trackListener(inputEventId + 1, motionMoveTimeline.eventTime,
+ motionMoveTimeline.readTime, DEVICE_ID, motionMoveTimeline.sources,
+ AMOTION_EVENT_ACTION_MOVE, InputEventType::MOTION);
+ mTracker->trackListener(inputEventId + 2, motionUpTimeline.eventTime, motionUpTimeline.readTime,
+ DEVICE_ID, motionUpTimeline.sources, AMOTION_EVENT_ACTION_UP,
+ InputEventType::MOTION);
+ mTracker->trackListener(inputEventId + 3, keyDownTimeline.eventTime, keyDownTimeline.readTime,
+ DEVICE_ID, keyDownTimeline.sources, AKEY_EVENT_ACTION_DOWN,
+ InputEventType::KEY);
+ mTracker->trackListener(inputEventId + 4, keyUpTimeline.eventTime, keyUpTimeline.readTime,
+ DEVICE_ID, keyUpTimeline.sources, AKEY_EVENT_ACTION_UP,
+ InputEventType::KEY);
+ mTracker->trackListener(inputEventId + 5, unknownTimeline.eventTime, unknownTimeline.readTime,
+ DEVICE_ID, unknownTimeline.sources, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ InputEventType::MOTION);
+
+ triggerEventReporting(unknownTimeline.eventTime);
+
+ std::vector<InputEventTimeline> expectedTimelines = {motionDownTimeline, motionMoveTimeline,
+ motionUpTimeline, keyDownTimeline,
+ keyUpTimeline, unknownTimeline};
+ assertReceivedTimelines(expectedTimelines);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index c57c251..9a6b266 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -99,11 +99,8 @@
setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
// reset current slot at the beginning
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
- .WillRepeatedly([](int32_t, int32_t, int32_t* outValue) {
- *outValue = 0;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
+ .WillRepeatedly(Return(0));
// mark all slots not in use
mockSlotValues({});
@@ -210,11 +207,8 @@
const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords;
// On buffer overflow mapper will be reset and MT slots data will be repopulated
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
- .WillRepeatedly([=](int32_t, int32_t, int32_t* outValue) {
- *outValue = 1;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
+ .WillRepeatedly(Return(1));
mockSlotValues(
{{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}});
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index a1279ff..411c7ba 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -196,7 +196,6 @@
TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
const std::vector<NotifyArgs>
allArgs{NotifyInputDevicesChangedArgs{},
- NotifyConfigurationChangedArgs{},
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(),
MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(FIRST_TOUCH_POINTER)
@@ -214,9 +213,6 @@
[&](const NotifyInputDevicesChangedArgs& args) {
mTestListener.assertNotifyInputDevicesChangedWasCalled();
},
- [&](const NotifyConfigurationChangedArgs& args) {
- mTestListener.assertNotifyConfigurationChangedWasCalled();
- },
[&](const NotifyKeyArgs& args) {
mTestListener.assertNotifyKeyWasCalled();
},
@@ -982,6 +978,36 @@
assertPointerControllerRemoved(pc);
}
+/**
+ * When both "show touches" and "stylus hover icons" are enabled, if the app doesn't specify an
+ * icon for the hovering stylus, fall back to using the spot hover icon.
+ */
+TEST_F(PointerChoreographerTest, ShowTouchesOverridesUnspecifiedStylusIcon) {
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ DISPLAY_ID)}});
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
+
+ mChoreographer.setPointerIcon(PointerIconStyle::TYPE_ARROW, DISPLAY_ID, DEVICE_ID);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_ARROW);
+
+ mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
+}
+
using StylusFixtureParam =
std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>;
@@ -2365,7 +2391,13 @@
assertPointerControllerRemoved(pc);
}
-class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest {
+using PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam =
+ std::tuple<std::string_view /*name*/, uint32_t /*source*/>;
+
+class PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture
+ : public PointerChoreographerTest,
+ public testing::WithParamInterface<
+ PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam> {
protected:
const std::unordered_map<int32_t, int32_t>
mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON},
@@ -2429,15 +2461,28 @@
}
};
-TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) {
+INSTANTIATE_TEST_SUITE_P(
+ PointerChoreographerTest, PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ testing::Values(std::make_tuple("Mouse", AINPUT_SOURCE_MOUSE),
+ std::make_tuple("Touchpad", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD)),
+ [](const testing::TestParamInfo<
+ PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ KeystrokesWithoutImeConnectionDoesNotHidePointerOrDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0);
+
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A);
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT);
@@ -2445,16 +2490,19 @@
ASSERT_TRUE(pc->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ AlphanumericKeystrokesWithImeConnectionHidePointerAndDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(2);
notifyKey(DISPLAY_ID, AKEYCODE_0);
ASSERT_FALSE(pc->isPointerShown());
@@ -2465,17 +2513,19 @@
ASSERT_FALSE(pc->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ MetaKeystrokesDoNotHidePointerOrDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0,
- {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0);
const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT,
AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT,
@@ -2491,14 +2541,16 @@
ASSERT_TRUE(pc->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplayAndDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
mChoreographer.setFocusedDisplay(DISPLAY_ID);
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID),
+ {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE);
auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -2506,6 +2558,7 @@
ASSERT_TRUE(pc2->isPointerShown());
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(2);
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
ASSERT_FALSE(pc1->isPointerShown());
@@ -2517,16 +2570,19 @@
ASSERT_TRUE(pc2->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCombinations) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
- // meta key combinations that should hide pointer
+ // meta key combinations that should hide pointer and disable touchpad taps
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(5);
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT);
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT);
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK);
@@ -2534,6 +2590,7 @@
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK);
// meta key combinations that should not hide pointer
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0);
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT);
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT);
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT);
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
new file mode 100644
index 0000000..6607bc7
--- /dev/null
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+#include "RotaryEncoderInputMapper.h"
+
+#include <list>
+#include <string>
+#include <tuple>
+#include <variant>
+
+#include <android-base/logging.h>
+#include <android_companion_virtualdevice_flags.h>
+#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
+#include <linux/input-event-codes.h>
+#include <linux/input.h>
+#include <utils/Timers.h>
+
+#include "InputMapperTest.h"
+#include "InputReaderBase.h"
+#include "InterfaceMocks.h"
+#include "NotifyArgs.h"
+#include "TestEventMatchers.h"
+#include "ui/Rotation.h"
+
+#define TAG "RotaryEncoderInputMapper_test"
+
+namespace android {
+
+using testing::AllOf;
+using testing::Return;
+using testing::VariantWith;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+
+namespace {
+
+DisplayViewport createViewport() {
+ DisplayViewport v;
+ v.orientation = ui::Rotation::Rotation0;
+ v.logicalRight = DISPLAY_HEIGHT;
+ v.logicalBottom = DISPLAY_WIDTH;
+ v.physicalRight = DISPLAY_HEIGHT;
+ v.physicalBottom = DISPLAY_WIDTH;
+ v.deviceWidth = DISPLAY_HEIGHT;
+ v.deviceHeight = DISPLAY_WIDTH;
+ v.isActive = true;
+ return v;
+}
+
+DisplayViewport createPrimaryViewport() {
+ DisplayViewport v = createViewport();
+ v.displayId = DISPLAY_ID;
+ v.uniqueId = "local:1";
+ return v;
+}
+
+DisplayViewport createSecondaryViewport() {
+ DisplayViewport v = createViewport();
+ v.displayId = SECONDARY_DISPLAY_ID;
+ v.uniqueId = "local:2";
+ v.type = ViewportType::EXTERNAL;
+ return v;
+}
+
+} // namespace
+
+namespace vd_flags = android::companion::virtualdevice::flags;
+
+/**
+ * Unit tests for RotaryEncoderInputMapper.
+ */
+class RotaryEncoderInputMapperTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override { SetUpWithBus(BUS_USB); }
+ void SetUpWithBus(int bus) override {
+ InputMapperUnitTest::SetUpWithBus(bus);
+
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
+ }
+};
+
+TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdWithAssociatedViewport) {
+ DisplayViewport primaryViewport = createPrimaryViewport();
+ DisplayViewport secondaryViewport = createSecondaryViewport();
+ mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
+
+ // Set up the secondary display as the associated viewport of the mapper.
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ // Ensure input events are generated for the secondary display.
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithDisplayId(SECONDARY_DISPLAY_ID)))));
+}
+
+TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdNoAssociatedViewport) {
+ // Set up the default display.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(createPrimaryViewport());
+
+ // Set up the mapper with no associated viewport.
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ // Ensure input events are generated without display ID
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithDisplayId(ui::LogicalDisplayId::INVALID)))));
+}
+
+TEST_F(RotaryEncoderInputMapperTest, ProcessRegularScroll) {
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f)))));
+}
+
+TEST_F(RotaryEncoderInputMapperTest, ProcessHighResScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
+}
+
+TEST_F(RotaryEncoderInputMapperTest, HighResScrollIgnoresRegularScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/SwitchInputMapper_test.cpp b/services/inputflinger/tests/SwitchInputMapper_test.cpp
new file mode 100644
index 0000000..ebbf10b
--- /dev/null
+++ b/services/inputflinger/tests/SwitchInputMapper_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "SwitchInputMapper.h"
+
+#include <list>
+#include <variant>
+
+#include <NotifyArgs.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <linux/input-event-codes.h>
+
+#include "InputMapperTest.h"
+#include "TestConstants.h"
+
+namespace android {
+
+class SwitchInputMapperTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ mMapper = createInputMapper<SwitchInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+ }
+};
+
+TEST_F(SwitchInputMapperTest, GetSources) {
+ ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mMapper->getSources());
+}
+
+TEST_F(SwitchInputMapperTest, GetSwitchState) {
+ setSwitchState(1, {SW_LID});
+ ASSERT_EQ(1, mMapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+
+ setSwitchState(0, {SW_LID});
+ ASSERT_EQ(0, mMapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+}
+
+TEST_F(SwitchInputMapperTest, Process) {
+ std::list<NotifyArgs> out;
+ out = process(ARBITRARY_TIME, EV_SW, SW_LID, 1);
+ ASSERT_TRUE(out.empty());
+ out = process(ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+ ASSERT_TRUE(out.empty());
+ out = process(ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+ ASSERT_TRUE(out.empty());
+ out = process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(1u, out.size());
+ const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin());
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
+ ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
+ args.switchMask);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
index ad48b0f..d2337dd 100644
--- a/services/inputflinger/tests/TestConstants.h
+++ b/services/inputflinger/tests/TestConstants.h
@@ -24,6 +24,12 @@
using std::chrono_literals::operator""ms;
+// Timeout for waiting for an input device to be added and processed
+static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 5000ms;
+
+// Timeout for asserting that an input device change did not occur
+static constexpr std::chrono::duration INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT = 100ms;
+
// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index a6d9d5b..6fa3365 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -615,7 +615,12 @@
explicit WithPointerIdMatcher(size_t index, int32_t pointerId)
: mIndex(index), mPointerId(pointerId) {}
- bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream* os) const {
+ if (mIndex >= args.pointerCoords.size()) {
+ *os << "Pointer index " << mIndex << " is out of bounds";
+ return false;
+ }
+
return args.pointerProperties[mIndex].id == mPointerId;
}
@@ -646,12 +651,51 @@
return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
}
-MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
- const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
- << ", " << argY << ")";
- return argX == x && argY == y;
+/// Relative motion matcher
+class WithRelativeMotionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithRelativeMotionMatcher(size_t pointerIndex, float relX, float relY)
+ : mPointerIndex(pointerIndex), mRelX(relX), mRelY(relY) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ if (mPointerIndex >= event.pointerCoords.size()) {
+ *os << "Pointer index " << mPointerIndex << " is out of bounds";
+ return false;
+ }
+
+ const PointerCoords& coords = event.pointerCoords[mPointerIndex];
+ bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) &&
+ mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ if (!matches) {
+ *os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
+ << mPointerIndex << ", but got ("
+ << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) << ", "
+ << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y) << ")";
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
+ << mPointerIndex;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong relative motion"; }
+
+private:
+ const size_t mPointerIndex;
+ const float mRelX;
+ const float mRelY;
+};
+
+inline WithRelativeMotionMatcher WithRelativeMotion(float relX, float relY) {
+ return WithRelativeMotionMatcher(0, relX, relY);
+}
+
+inline WithRelativeMotionMatcher WithPointerRelativeMotion(size_t pointerIndex, float relX,
+ float relY) {
+ return WithRelativeMotionMatcher(pointerIndex, relX, relY);
}
MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
@@ -720,6 +764,21 @@
return argDistance == distance;
}
+MATCHER_P(WithScroll, scroll, "InputEvent with specified scroll value") {
+ const auto argScroll = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SCROLL);
+ *result_listener << "expected scroll value " << scroll << ", but got " << argScroll;
+ return argScroll == scroll;
+}
+
+MATCHER_P2(WithScroll, scrollX, scrollY, "InputEvent with specified scroll values") {
+ const auto argScrollX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_HSCROLL);
+ const auto argScrollY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_VSCROLL);
+ *result_listener << "expected scroll values " << scrollX << " scroll x " << scrollY
+ << " scroll y, but got " << argScrollX << " scroll x " << argScrollY
+ << " scroll y";
+ return argScrollX == scrollX && argScrollY == scrollY;
+}
+
MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
@@ -743,10 +802,14 @@
return argToolType == toolType;
}
-MATCHER_P2(WithPointerToolType, pointer, toolType,
+MATCHER_P2(WithPointerToolType, pointerIndex, toolType,
"InputEvent with specified tool type for pointer") {
- const auto argToolType = arg.pointerProperties[pointer].toolType;
- *result_listener << "expected pointer " << pointer << " to have tool type "
+ if (std::cmp_greater_equal(pointerIndex, arg.getPointerCount())) {
+ *result_listener << "Pointer index " << pointerIndex << " is out of bounds";
+ return false;
+ }
+ const auto argToolType = arg.pointerProperties[pointerIndex].toolType;
+ *result_listener << "expected pointer " << pointerIndex << " to have tool type "
<< ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
return argToolType == toolType;
}
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 41e250f..369f9cc 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -37,19 +37,6 @@
"to have been called."));
}
-void TestInputListener::assertNotifyConfigurationChangedWasCalled(
- NotifyConfigurationChangedArgs* outEventArgs) {
- ASSERT_NO_FATAL_FAILURE(
- assertCalled<NotifyConfigurationChangedArgs>(outEventArgs,
- "Expected notifyConfigurationChanged() "
- "to have been called."));
-}
-
-void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
- ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>(
- "notifyConfigurationChanged() should not be called."));
-}
-
void TestInputListener::assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs) {
ASSERT_NO_FATAL_FAILURE(
assertCalled<
@@ -192,10 +179,6 @@
addToQueue<NotifyInputDevicesChangedArgs>(args);
}
-void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- addToQueue<NotifyConfigurationChangedArgs>(args);
-}
-
void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
addToQueue<NotifyDeviceResetArgs>(args);
}
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 3c5e014..47eae4d 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -38,11 +38,6 @@
void assertNotifyInputDevicesChangedWasCalled(
NotifyInputDevicesChangedArgs* outEventArgs = nullptr);
- void assertNotifyConfigurationChangedWasCalled(
- NotifyConfigurationChangedArgs* outEventArgs = nullptr);
-
- void assertNotifyConfigurationChangedWasNotCalled();
-
void clearNotifyDeviceResetCalls();
void assertNotifyDeviceResetWasCalled(const ::testing::Matcher<NotifyDeviceResetArgs>& matcher);
@@ -85,8 +80,6 @@
virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
-
virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
virtual void notifyKey(const NotifyKeyArgs& args) override;
@@ -107,7 +100,6 @@
const std::chrono::milliseconds mEventDidNotHappenTimeout;
std::tuple<std::vector<NotifyInputDevicesChangedArgs>, //
- std::vector<NotifyConfigurationChangedArgs>, //
std::vector<NotifyDeviceResetArgs>, //
std::vector<NotifyKeyArgs>, //
std::vector<NotifyMotionArgs>, //
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 12fa835..ea69fff 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -103,11 +103,8 @@
setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_))
- .WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) {
- *outValue = 0;
- return OK;
- });
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT))
+ .WillRepeatedly(Return(0));
EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_))
.WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
return base::ResultError("Axis not supported", NAME_NOT_FOUND);
@@ -175,4 +172,22 @@
ASSERT_THAT(args, testing::IsEmpty());
}
+TEST_F(TouchpadInputMapperTest, TouchpadHardwareState) {
+ mReaderConfiguration.shouldNotifyTouchpadHardwareState = true;
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+
+ args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
+ args += process(EV_KEY, BTN_TOUCH, 1);
+ setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
+ args += process(EV_KEY, BTN_TOOL_FINGER, 1);
+ args += process(EV_ABS, ABS_MT_POSITION_X, 50);
+ args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
+ args += process(EV_ABS, ABS_MT_PRESSURE, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ mFakePolicy->assertTouchpadHardwareStateNotified();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 853f628..bbb2fc8 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -414,20 +414,6 @@
};
/**
- * Create a basic configuration change and send it to input processor.
- * Expect that the event is received by the next input stage, unmodified.
- */
-TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) {
- // Create a basic configuration change and send to blocker
- NotifyConfigurationChangedArgs args(/*sequenceNum=*/1, /*eventTime=*/2);
-
- mBlocker->notifyConfigurationChanged(args);
- NotifyConfigurationChangedArgs outArgs;
- ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
- ASSERT_EQ(args, outArgs);
-}
-
-/**
* Keys are not handled in 'UnwantedInteractionBlocker' and should be passed
* to next stage unmodified.
*/
diff --git a/services/inputflinger/tests/VibratorInputMapper_test.cpp b/services/inputflinger/tests/VibratorInputMapper_test.cpp
new file mode 100644
index 0000000..6e3344c
--- /dev/null
+++ b/services/inputflinger/tests/VibratorInputMapper_test.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include "VibratorInputMapper.h"
+
+#include <chrono>
+#include <list>
+#include <variant>
+#include <vector>
+
+#include <EventHub.h>
+#include <NotifyArgs.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+
+#include "InputMapperTest.h"
+#include "VibrationElement.h"
+
+namespace android {
+
+class VibratorInputMapperTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(testing::Return(InputDeviceClass::VIBRATOR));
+ EXPECT_CALL(mMockEventHub, getVibratorIds(EVENTHUB_ID))
+ .WillRepeatedly(testing::Return<std::vector<int32_t>>({0, 1}));
+ mMapper = createInputMapper<VibratorInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+ }
+};
+
+TEST_F(VibratorInputMapperTest, GetSources) {
+ ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mMapper->getSources());
+}
+
+TEST_F(VibratorInputMapperTest, GetVibratorIds) {
+ ASSERT_EQ(mMapper->getVibratorIds().size(), 2U);
+}
+
+TEST_F(VibratorInputMapperTest, Vibrate) {
+ constexpr uint8_t DEFAULT_AMPLITUDE = 192;
+ constexpr int32_t VIBRATION_TOKEN = 100;
+
+ VibrationElement pattern(2);
+ VibrationSequence sequence(2);
+ pattern.duration = std::chrono::milliseconds(200);
+ pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 2},
+ {/*vibratorId=*/1, DEFAULT_AMPLITUDE}};
+ sequence.addElement(pattern);
+ pattern.duration = std::chrono::milliseconds(500);
+ pattern.channels = {{/*vibratorId=*/0, DEFAULT_AMPLITUDE / 4},
+ {/*vibratorId=*/1, DEFAULT_AMPLITUDE}};
+ sequence.addElement(pattern);
+
+ std::vector<int64_t> timings = {0, 1};
+ std::vector<uint8_t> amplitudes = {DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE / 2};
+
+ ASSERT_FALSE(mMapper->isVibrating());
+ // Start vibrating
+ std::list<NotifyArgs> out = mMapper->vibrate(sequence, /*repeat=*/-1, VIBRATION_TOKEN);
+ ASSERT_TRUE(mMapper->isVibrating());
+ // Verify vibrator state listener was notified.
+ ASSERT_EQ(1u, out.size());
+ const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+ ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId);
+ ASSERT_TRUE(vibrateArgs.isOn);
+ // Stop vibrating
+ out = mMapper->cancelVibrate(VIBRATION_TOKEN);
+ ASSERT_FALSE(mMapper->isVibrating());
+ // Verify vibrator state listener was notified.
+ ASSERT_EQ(1u, out.size());
+ const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+ ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId);
+ ASSERT_FALSE(cancelArgs.isOn);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index 0b4ac1f..46a6189 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -39,12 +39,6 @@
while (fdp.remaining_bytes() > 0) {
fdp.PickValueInArray<std::function<void()>>({
[&]() -> void {
- // SendToNextStage_NotifyConfigurationChangedArgs
- mClassifier->notifyConfigurationChanged(
- {/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
- /*eventTime=*/fdp.ConsumeIntegral<nsecs_t>()});
- },
- [&]() -> void {
// SendToNextStage_NotifyKeyArgs
const nsecs_t eventTime =
fdp.ConsumeIntegralInRange<nsecs_t>(0,
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 7d26a43..5442a65 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -117,6 +117,10 @@
return reader->getSensors(deviceId);
}
+ std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) {
+ return reader->getTouchpadHardwareProperties(deviceId);
+ }
+
bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) {
return reader->canDispatchToDisplay(deviceId, displayId);
}
@@ -151,10 +155,6 @@
return reader->getLightPlayerId(deviceId, lightId);
}
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
- reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
- }
-
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
@@ -169,6 +169,8 @@
DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); }
+ void notifyMouseCursorFadedOnTyping() override { reader->notifyMouseCursorFadedOnTyping(); }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 6daeaaf..695eb3c 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -18,6 +18,7 @@
#include <linux/input.h>
#include "../../InputDeviceMetricsSource.h"
+#include "../InputEventTimeline.h"
#include "dispatcher/LatencyTracker.h"
namespace android {
@@ -65,14 +66,15 @@
fdp.PickValueInArray<std::function<void()>>({
[&]() -> void {
int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
- int32_t isDown = fdp.ConsumeBool();
nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
std::set<InputDeviceUsageSource> sources = {
fdp.ConsumeEnum<InputDeviceUsageSource>()};
- tracker.trackListener(inputEventId, isDown, eventTime, readTime, deviceId,
- sources);
+ const int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>();
+ const InputEventType inputEventType = fdp.ConsumeEnum<InputEventType>();
+ tracker.trackListener(inputEventId, eventTime, readTime, deviceId, sources,
+ inputEventActionType, inputEventType);
},
[&]() -> void {
int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index ff425dd..fa8270a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -17,6 +17,7 @@
#include <map>
#include <memory>
+#include <optional>
#include <EventHub.h>
#include <InputDevice.h>
@@ -31,8 +32,7 @@
EV_MSC,
EV_REL,
android::EventHubInterface::DEVICE_ADDED,
- android::EventHubInterface::DEVICE_REMOVED,
- android::EventHubInterface::FINISHED_DEVICE_SCAN};
+ android::EventHubInterface::DEVICE_REMOVED};
constexpr size_t kValidCodes[] = {
SYN_REPORT,
@@ -119,16 +119,25 @@
void setAbsoluteAxisInfo(int32_t deviceId, int axis, const RawAbsoluteAxisInfo& axisInfo) {
mAxes[deviceId][axis] = axisInfo;
}
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override {
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t deviceId,
+ int axis) const override {
if (auto deviceAxesIt = mAxes.find(deviceId); deviceAxesIt != mAxes.end()) {
const std::map<int, RawAbsoluteAxisInfo>& deviceAxes = deviceAxesIt->second;
if (auto axisInfoIt = deviceAxes.find(axis); axisInfoIt != deviceAxes.end()) {
- *outAxisInfo = axisInfoIt->second;
- return OK;
+ return axisInfoIt->second;
}
}
- return mFdp->ConsumeIntegral<status_t>();
+ if (mFdp->ConsumeBool()) {
+ return std::optional<RawAbsoluteAxisInfo>({
+ .minValue = mFdp->ConsumeIntegral<int32_t>(),
+ .maxValue = mFdp->ConsumeIntegral<int32_t>(),
+ .flat = mFdp->ConsumeIntegral<int32_t>(),
+ .fuzz = mFdp->ConsumeIntegral<int32_t>(),
+ .resolution = mFdp->ConsumeIntegral<int32_t>(),
+ });
+ } else {
+ return std::nullopt;
+ }
}
bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
bool hasInputProperty(int32_t deviceId, int property) const override {
@@ -193,13 +202,17 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
+ void setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const override {}
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override {
- return mFdp->ConsumeIntegral<status_t>();
+ std::optional<int32_t> getAbsoluteAxisValue(int32_t deviceId, int32_t axis) const override {
+ if (mFdp->ConsumeBool()) {
+ return mFdp->ConsumeIntegral<int32_t>();
+ } else {
+ return std::nullopt;
+ }
}
base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
size_t slotCount) const override {
@@ -269,6 +282,9 @@
FuzzInputReaderPolicy(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
+ void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) override {}
+ void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> layoutInfo) override {
@@ -293,7 +309,6 @@
class FuzzInputListener : public virtual InputListenerInterface {
public:
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override {}
- void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override {}
void notifyKey(const NotifyKeyArgs& args) override {}
void notifyMotion(const NotifyMotionArgs& args) override {}
void notifySwitch(const NotifySwitchArgs& args) override {}
@@ -333,8 +348,8 @@
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
- void setPreventingTouchpadTaps(bool prevent) {}
- bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
+ void setPreventingTouchpadTaps(bool prevent) override {}
+ bool isPreventingTouchpadTaps() override { return mFdp->ConsumeBool(); };
void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; };
nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; };
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index c620032..ebbb311 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -34,7 +34,6 @@
if (fdp.ConsumeBool()) {
eventHub.setAbsoluteAxisInfo(id, axis,
RawAbsoluteAxisInfo{
- .valid = fdp.ConsumeBool(),
.minValue = fdp.ConsumeIntegral<int32_t>(),
.maxValue = fdp.ConsumeIntegral<int32_t>(),
.flat = fdp.ConsumeIntegral<int32_t>(),
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index f4b0265..7b2596a 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -52,6 +52,7 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-Wthread-safety",
"-fvisibility=hidden",
],
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 555b80a..33724a9 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -26,12 +26,17 @@
using util::ProtoOutputStream;
-SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
- uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId)
- : mService(service), mUid(uid), mMem(*mem),
+SensorService::SensorDirectConnection::SensorDirectConnection(
+ const sp<SensorService>& service, uid_t uid, pid_t pid, const sensors_direct_mem_t* mem,
+ int32_t halChannelHandle, const String16& opPackageName, int deviceId)
+ : mService(service),
+ mUid(uid),
+ mPid(pid),
+ mMem(*mem),
mHalChannelHandle(halChannelHandle),
- mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
+ mOpPackageName(opPackageName),
+ mDeviceId(deviceId),
+ mDestroyed(false) {
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -62,10 +67,21 @@
void SensorService::SensorDirectConnection::dump(String8& result) const {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
- String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size());
- for (auto &i : mActivated) {
- result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
+ result.appendFormat("\t%s | HAL channel handle %d | uid %d | pid %d\n",
+ String8(mOpPackageName).c_str(), getHalChannelHandle(), mUid, mPid);
+ result.appendFormat("\tActivated sensor count: %zu\n", mActivated.size());
+ dumpSensorInfoWithLock(result, mActivated);
+
+ result.appendFormat("\tBackup sensor (opened but UID idle) count: %zu\n",
+ mActivatedBackup.size());
+ dumpSensorInfoWithLock(result, mActivatedBackup);
+}
+
+void SensorService::SensorDirectConnection::dumpSensorInfoWithLock(
+ String8& result, std::unordered_map<int, int> sensors) const {
+ for (auto& i : sensors) {
+ result.appendFormat("\t\t%s 0x%08x | rate %d\n", mService->getSensorName(i.first).c_str(),
+ i.first, i.second);
}
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index bfaf811..9f21731 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
#define ANDROID_SENSOR_DIRECT_CONNECTION_H
-#include <optional>
+#include <android-base/thread_annotations.h>
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <binder/BinderService.h>
@@ -37,15 +38,15 @@
class SensorService::SensorDirectConnection: public BnSensorEventConnection {
public:
- SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
- const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId);
+ SensorDirectConnection(const sp<SensorService>& service, uid_t uid, pid_t pid,
+ const sensors_direct_mem_t* mem, int32_t halChannelHandle,
+ const String16& opPackageName, int deviceId);
void dump(String8& result) const;
void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
const String16& getOpPackageName() const { return mOpPackageName; }
int32_t getHalChannelHandle() const;
- bool isEquivalent(const sensors_direct_mem_t *mem) const;
+ bool isEquivalent(const sensors_direct_mem_t* mem) const;
// Invoked when access to sensors for this connection has changed, e.g. lost or
// regained due to changes in the sensor restricted/privacy mode or the
@@ -94,8 +95,13 @@
// Recover sensor requests previously capped by capRates().
void uncapRates();
+ // Dumps a set of sensor infos.
+ void dumpSensorInfoWithLock(String8& result, std::unordered_map<int, int> sensors) const
+ EXCLUSIVE_LOCKS_REQUIRED(mConnectionLock);
+
const sp<SensorService> mService;
const uid_t mUid;
+ const pid_t mPid;
const sensors_direct_mem_t mMem;
const int32_t mHalChannelHandle;
const String16 mOpPackageName;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 760cc8f..0d00642 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -90,15 +90,14 @@
result.append("NORMAL\n");
}
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
- "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize,
- mMaxCacheSize);
+ "max cache size %d | has sensor access: %s\n",
+ mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize,
+ hasSensorAccess() ? "true" : "false");
for (auto& it : mSensorInfo) {
const FlushInfo& flushInfo = it.second;
- result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
- mService->getSensorName(it.first).c_str(),
- it.first,
- flushInfo.mFirstFlushPending ? "First flush pending" :
- "active",
+ result.appendFormat("\t %s 0x%08x | first flush pending: %s | pending flush events %d \n",
+ mService->getSensorName(it.first).c_str(), it.first,
+ flushInfo.mFirstFlushPending ? "true" : "false",
flushInfo.mPendingFlushEventsToSend);
}
#if DEBUG_CONNECTIONS
@@ -712,14 +711,17 @@
if (err == OK && isSensorCapped) {
if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
!isRateCappedBasedOnPermission()) {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
}
}
} else {
err = mService->disable(this, handle);
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup.erase(handle);
}
return err;
@@ -751,8 +753,10 @@
if (ret == OK && isSensorCapped) {
if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
!isRateCappedBasedOnPermission()) {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
}
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 6a98a40..bb8733d 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -199,7 +199,8 @@
// valid mapping for sensors that require a permission in order to reduce the lookup time.
std::unordered_map<int32_t, int32_t> mHandleToAppOp;
// Mapping of sensor handles to its rate before being capped by the mic toggle.
- std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup;
+ std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup
+ GUARDED_BY(mConnectionLock);
userid_t mUserId;
std::optional<bool> mIsRateCappedBasedOnPermission;
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index dc9e821..a8a773a 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -38,10 +38,11 @@
mActivated = false;
}
- SensorRegistrationInfo(int32_t handle, const String8 &packageName,
- int64_t samplingRateNs, int64_t maxReportLatencyNs, bool activate) {
+ SensorRegistrationInfo(int32_t handle, const String8& packageName, int64_t samplingRateNs,
+ int64_t maxReportLatencyNs, bool activate, status_t registerStatus) {
mSensorHandle = handle;
mPackageName = packageName;
+ mRegisteredStatus = registerStatus;
mSamplingRateUs = static_cast<int64_t>(samplingRateNs/1000);
mMaxReportLatencyUs = static_cast<int64_t>(maxReportLatencyNs/1000);
@@ -60,28 +61,43 @@
return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0);
}
- // Dumpable interface
- virtual std::string dump() const override {
+ std::string dump(SensorService* sensorService) const {
struct tm* timeinfo = localtime(&mRealtimeSec);
const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour);
const int8_t min = static_cast<int8_t>(timeinfo->tm_min);
const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec);
std::ostringstream ss;
- ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":"
- << std::setw(2) << static_cast<int>(min) << ":"
- << std::setw(2) << static_cast<int>(sec)
- << (mActivated ? " +" : " -")
- << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
- << std::setfill(' ') << " pid=" << std::setw(5) << mPid
- << " uid=" << std::setw(5) << mUid << " package=" << mPackageName;
- if (mActivated) {
- ss << " samplingPeriod=" << mSamplingRateUs << "us"
- << " batchingPeriod=" << mMaxReportLatencyUs << "us";
- };
+ ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":" << std::setw(2)
+ << static_cast<int>(min) << ":" << std::setw(2) << static_cast<int>(sec)
+ << (mActivated ? " +" : " -") << " 0x" << std::hex << std::setw(8) << mSensorHandle;
+ ss << std::dec << std::setfill(' ') << " pid=" << std::setw(5) << mPid
+ << " uid=" << std::setw(5) << mUid;
+
+ std::string samplingRate =
+ mActivated ? std::to_string(mSamplingRateUs) : " N/A ";
+ std::string batchingInterval =
+ mActivated ? std::to_string(mMaxReportLatencyUs) : " N/A ";
+ ss << " samplingPeriod=" << std::setfill(' ') << std::setw(8)
+ << samplingRate << "us" << " batchingPeriod=" << std::setfill(' ')
+ << std::setw(8) << batchingInterval << "us"
+ << " result=" << statusToString(mRegisteredStatus)
+ << " (sensor, package): (" << std::setfill(' ') << std::left
+ << std::setw(27);
+
+ if (sensorService != nullptr) {
+ ss << sensorService->getSensorName(mSensorHandle);
+ } else {
+ ss << "null";
+ }
+ ss << ", " << mPackageName << ")";
+
return ss.str();
}
+ // Dumpable interface
+ virtual std::string dump() const override { return dump(static_cast<SensorService*>(nullptr)); }
+
/**
* Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message
* using ProtoOutputStream.
@@ -110,6 +126,7 @@
int64_t mMaxReportLatencyUs;
bool mActivated;
time_t mRealtimeSec;
+ status_t mRegisteredStatus;
};
} // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 31b7f88..060508c 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -682,14 +682,14 @@
mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
const auto& activeConnections = connLock.getActiveConnections();
- result.appendFormat("%zd active connections\n", activeConnections.size());
+ result.appendFormat("%zd open event connections\n", activeConnections.size());
for (size_t i=0 ; i < activeConnections.size() ; i++) {
result.appendFormat("Connection Number: %zu \n", i);
activeConnections[i]->dump(result);
}
const auto& directConnections = connLock.getDirectConnections();
- result.appendFormat("%zd direct connections\n", directConnections.size());
+ result.appendFormat("%zd open direct connections\n", directConnections.size());
for (size_t i = 0 ; i < directConnections.size() ; i++) {
result.appendFormat("Direct connection %zu:\n", i);
directConnections[i]->dump(result);
@@ -708,7 +708,7 @@
SENSOR_REGISTRATIONS_BUF_SIZE;
continue;
}
- result.appendFormat("%s\n", reg_info.dump().c_str());
+ result.appendFormat("%s\n", reg_info.dump(this).c_str());
currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
SENSOR_REGISTRATIONS_BUF_SIZE;
} while(startIndex != currentIndex);
@@ -1583,7 +1583,11 @@
// Only 4 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION,
// REPLAY_DATA_INJECTION and HAL_BYPASS_REPLAY_DATA_INJECTION
if (requestedMode != NORMAL && !isInjectionMode(requestedMode)) {
- return nullptr;
+ ALOGE(
+ "Failed to create sensor event connection: invalid request mode. "
+ "requestMode: %d",
+ requestedMode);
+ return nullptr;
}
resetTargetSdkVersionCache(opPackageName);
@@ -1591,8 +1595,19 @@
// To create a client in DATA_INJECTION mode to inject data, SensorService should already be
// operating in DI mode.
if (requestedMode == DATA_INJECTION) {
- if (mCurrentOperatingMode != DATA_INJECTION) return nullptr;
- if (!isAllowListedPackage(packageName)) return nullptr;
+ if (mCurrentOperatingMode != DATA_INJECTION) {
+ ALOGE(
+ "Failed to create sensor event connection: sensor service not in "
+ "DI mode when creating a client in DATA_INJECTION mode");
+ return nullptr;
+ }
+ if (!isAllowListedPackage(packageName)) {
+ ALOGE(
+ "Failed to create sensor event connection: package %s not in "
+ "allowed list for DATA_INJECTION mode",
+ packageName.c_str());
+ return nullptr;
+ }
}
uid_t uid = IPCThreadState::self()->getCallingUid();
@@ -1729,7 +1744,10 @@
ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
} else {
mem.handle = clone;
- conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ conn = new SensorDirectConnection(this, uid, pid, &mem, channelHandle, opPackageName,
+ deviceId);
}
if (conn == nullptr) {
@@ -2149,17 +2167,17 @@
sensor->getSensor().getRequiredAppOp() >= 0) {
connection->mHandleToAppOp[handle] = sensor->getSensor().getRequiredAppOp();
}
-
- mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
- SensorRegistrationInfo(handle, connection->getPackageName(),
- samplingPeriodNs, maxBatchReportLatencyNs, true);
- mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
}
if (err != NO_ERROR) {
// batch/activate has failed, reset our state.
cleanupWithoutDisableLocked(connection, handle);
}
+
+ mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+ SensorRegistrationInfo(handle, connection->getPackageName(), samplingPeriodNs,
+ maxBatchReportLatencyNs, /*activate=*/ true, err);
+ mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
return err;
}
@@ -2172,13 +2190,10 @@
if (err == NO_ERROR) {
std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
-
}
- if (err == NO_ERROR) {
- mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
- SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, false);
- mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
- }
+ mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+ SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, /*activate=*/ false, err);
+ mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
return err;
}
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index f6f104e..b2dc89b 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -26,6 +26,11 @@
"libfakeservicemanager",
"libcutils",
"liblog",
+ "libsensor_flags_c_lib",
+ ],
+ shared_libs: [
+ "libaconfig_storage_read_api_cc",
+ "server_configurable_flags",
],
srcs: [
"fuzzer.cpp",
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 1b6c598..c2a9880 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -47,6 +47,7 @@
"libtimestats_deps",
"libsurfaceflinger_common_deps",
"surfaceflinger_defaults",
+ "libsurfaceflinger_proto_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
@@ -85,7 +86,7 @@
"libui",
"libutils",
"libSurfaceFlingerProp",
- "libaconfig_storage_read_api_cc"
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
"iinputflinger_aidl_lib_static",
@@ -93,7 +94,6 @@
"libcompositionengine",
"libframetimeline",
"libgui_aidl_static",
- "liblayers_proto",
"libperfetto_client_experimental",
"librenderengine",
"libscheduler",
@@ -187,6 +187,7 @@
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
"HdrSdrRatioOverlay.cpp",
+ "Jank/JankTracker.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 6b4215e..abeb2a9 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -21,7 +21,7 @@
#include <private/android_filesystem_config.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/SchedulingPolicy.h>
#include "Client.h"
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 09e41ff..40ea8d3 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -22,7 +22,7 @@
#include <cinttypes>
#include <android-base/stringprintf.h>
-#include <gui/TraceUtils.h>
+#include <common/trace.h>
#include <renderengine/impl/ExternalTexture.h>
#include "ClientCache.h"
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index d4f152d..141a228 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -17,6 +17,7 @@
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
+ "libsurfaceflinger_proto_deps",
],
cflags: [
"-DLOG_TAG=\"CompositionEngine\"",
@@ -41,7 +42,6 @@
"libutils",
],
static_libs: [
- "liblayers_proto",
"libmath",
"librenderengine",
"libtimestats",
@@ -147,6 +147,7 @@
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
+ "tests/HwcAsyncWorkerTest.cpp",
"tests/HwcBufferCacheTest.cpp",
"tests/MockHWC2.cpp",
"tests/MockHWComposer.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 11759b8..d1429a2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -35,6 +35,7 @@
#pragma clang diagnostic ignored "-Wextra"
#include <gui/BufferQueue.h>
+#include <ui/EdgeExtensionEffect.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
#include <ui/StretchEffect.h>
@@ -133,12 +134,16 @@
// The bounds of the layer in layer local coordinates
FloatRect geomLayerBounds;
+ // The crop to apply to the layer in layer local coordinates
+ FloatRect geomLayerCrop;
+
ShadowSettings shadowSettings;
// List of regions that require blur
std::vector<BlurRegion> blurRegions;
StretchEffect stretchEffect;
+ EdgeExtensionEffect edgeExtensionEffect;
/*
* Geometry state
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index bdaa1d0..d9018bc 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -37,7 +37,8 @@
lhs.colorTransform == rhs.colorTransform &&
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
- lhs.stretchEffect == rhs.stretchEffect;
+ lhs.stretchEffect == rhs.stretchEffect &&
+ lhs.edgeExtensionEffect == rhs.edgeExtensionEffect;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 4c77687..5c5d0cd 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <common/trace.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
@@ -23,7 +24,6 @@
#include <ui/DisplayMap.h>
#include <renderengine/RenderEngine.h>
-#include <utils/Trace.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -128,7 +128,7 @@
} // namespace
void CompositionEngine::present(CompositionRefreshArgs& args) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
preComposition(args);
@@ -155,7 +155,7 @@
}
{
- ATRACE_NAME("Waiting on HWC");
+ SFTRACE_NAME("Waiting on HWC");
for (auto& future : presentFutures) {
// TODO(b/185536303): Call ftl::Future::wait() once it exists, since
// we do not need the return value of get().
@@ -177,7 +177,7 @@
}
void CompositionEngine::preComposition(CompositionRefreshArgs& args) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
bool needsAnotherUpdate = false;
@@ -199,7 +199,7 @@
// promises for buffer releases are fulfilled at the end of composition.
void CompositionEngine::postComposition(CompositionRefreshArgs& args) {
if (FlagManager::getInstance().ce_fence_promise()) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
for (auto& layerFE : args.layers) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index c1617d7..77b1940 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -15,6 +15,7 @@
*/
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
@@ -25,9 +26,6 @@
#include <compositionengine/impl/DumpHelpers.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/RenderSurface.h>
-#include <gui/TraceUtils.h>
-
-#include <utils/Trace.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -235,7 +233,7 @@
bool Display::chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
- ATRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str());
+ SFTRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str());
ALOGV(__FUNCTION__);
if (mIsDisconnected) {
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
index 6086f0b..91385b4 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -24,6 +24,7 @@
#include <android-base/thread_annotations.h>
#include <cutils/sched_policy.h>
+#include <ftl/fake_guard.h>
namespace android::compositionengine::impl {
@@ -60,7 +61,7 @@
std::unique_lock<std::mutex> lock(mMutex);
android::base::ScopedLockAssertion assumeLock(mMutex);
while (!mDone) {
- mCv.wait(lock);
+ mCv.wait(lock, [this]() FTL_FAKE_GUARD(mMutex) { return mTaskRequested || mDone; });
if (mTaskRequested && mTask.valid()) {
mTask();
mTaskRequested = false;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b40aea4..2d8f98f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -17,6 +17,7 @@
#include <SurfaceFlingerProperties.sysprop.h>
#include <android-base/stringprintf.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -31,7 +32,6 @@
#include <compositionengine/impl/planner/Planner.h>
#include <ftl/algorithm.h>
#include <ftl/future.h>
-#include <gui/TraceUtils.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/Time.h>
@@ -53,7 +53,6 @@
#include <android-base/properties.h>
#include <ui/DebugUtils.h>
#include <ui/HdrCapabilities.h>
-#include <utils/Trace.h>
#include "TracedOrdinal.h"
@@ -424,7 +423,7 @@
void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& geomSnapshots) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
rebuildLayerStacks(refreshArgs, geomSnapshots);
@@ -453,8 +452,8 @@
})
.value();
};
- ATRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(),
- stringifyExpectedPresentTime().c_str());
+ SFTRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(),
+ stringifyExpectedPresentTime().c_str());
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
@@ -518,7 +517,7 @@
if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
// Process the layers to determine visibility and coverage
@@ -804,7 +803,7 @@
}
void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
if (!getState().isEnabled) {
@@ -831,14 +830,14 @@
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
mPlanner->plan(getOutputLayersOrderedByZ());
}
void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
if (!getState().isEnabled) {
@@ -1081,7 +1080,7 @@
}
void Output::prepareFrame() {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
auto& outputState = editState();
@@ -1102,10 +1101,10 @@
}
ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) {
- return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
+ return ftl::Future<bool>(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
presentFrameAndReleaseLayers(flushEvenWhenDisabled);
return true;
- })))
+ }))
.then([](bool) { return std::monostate{}; });
}
@@ -1116,7 +1115,7 @@
}
GpuCompositionResult Output::prepareFrameAsync() {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
auto& state = editState();
const auto& previousChanges = state.previousDeviceRequestedChanges;
@@ -1146,7 +1145,7 @@
state.strategyPrediction = predictionSucceeded ? CompositionStrategyPredictionState::SUCCESS
: CompositionStrategyPredictionState::FAIL;
if (!predictionSucceeded) {
- ATRACE_NAME("CompositionStrategyPredictionMiss");
+ SFTRACE_NAME("CompositionStrategyPredictionMiss");
resetCompositionStrategy();
if (chooseCompositionSuccess) {
applyCompositionStrategy(changes);
@@ -1155,7 +1154,7 @@
// Track the dequeued buffer to reuse so we don't need to dequeue another one.
compositionResult.buffer = buffer;
} else {
- ATRACE_NAME("CompositionStrategyPredictionHit");
+ SFTRACE_NAME("CompositionStrategyPredictionHit");
}
state.previousDeviceRequestedChanges = std::move(changes);
state.previousDeviceRequestedSuccess = chooseCompositionSuccess;
@@ -1187,7 +1186,7 @@
}
void Output::finishFrame(GpuCompositionResult&& result) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
const auto& outputState = getState();
if (!outputState.isEnabled) {
@@ -1276,7 +1275,7 @@
std::optional<base::unique_fd> Output::composeSurfaces(
const Region& debugRegion, std::shared_ptr<renderengine::ExternalTexture> tex,
base::unique_fd& fd) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV(__FUNCTION__);
const auto& outputState = getState();
@@ -1317,13 +1316,13 @@
if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
clientCompositionDisplay,
clientCompositionLayers)) {
- ATRACE_NAME("ClientCompositionCacheHit");
+ SFTRACE_NAME("ClientCompositionCacheHit");
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
// b/239944175 pass the fence associated with the buffer.
return base::unique_fd(std::move(fd));
}
- ATRACE_NAME("ClientCompositionCacheMiss");
+ SFTRACE_NAME("ClientCompositionCacheMiss");
mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
clientCompositionLayers);
}
@@ -1570,7 +1569,7 @@
}
void Output::presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) {
- ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
+ SFTRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
if (!getState().isEnabled) {
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index c0b23d9..d6028bf 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -18,6 +18,7 @@
#include <android-base/stringprintf.h>
#include <android/native_window.h>
+#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplaySurface.h>
@@ -32,7 +33,6 @@
#include <system/window.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
-#include <utils/Trace.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -149,7 +149,7 @@
std::shared_ptr<renderengine::ExternalTexture> RenderSurface::dequeueBuffer(
base::unique_fd* bufferFence) {
- ATRACE_CALL();
+ SFTRACE_CALL();
int fd = -1;
ANativeWindowBuffer* buffer = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ea9442d..409a206 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -21,6 +21,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <math/HashCombine.h>
@@ -28,7 +29,6 @@
#include <renderengine/RenderEngine.h>
#include <ui/DebugUtils.h>
#include <ui/HdrRenderTypeUtils.h>
-#include <utils/Trace.h>
namespace android::compositionengine::impl::planner {
@@ -160,7 +160,7 @@
void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
const OutputCompositionState& outputState,
bool deviceHandlesColorTransform) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (outputState.powerCallback) {
outputState.powerCallback->notifyCpuLoadUp();
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 4bafed2..783209c 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -21,11 +21,10 @@
#include <android-base/properties.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
-#include <gui/TraceUtils.h>
-
using time_point = std::chrono::steady_clock::time_point;
using namespace std::chrono_literals;
@@ -77,7 +76,7 @@
NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
NonBufferHash hash, time_point now) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
mUnflattenedDisplayCost += unflattenedDisplayCost;
@@ -113,7 +112,7 @@
const OutputCompositionState& outputState,
std::optional<std::chrono::steady_clock::time_point> renderDeadline,
bool deviceHandlesColorTransform) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!mNewCachedSet) {
return;
@@ -121,7 +120,7 @@
// Ensure that a cached set has a valid buffer first
if (mNewCachedSet->hasRenderedBuffer()) {
- ATRACE_NAME("mNewCachedSet->hasRenderedBuffer()");
+ SFTRACE_NAME("mNewCachedSet->hasRenderedBuffer()");
return;
}
@@ -138,13 +137,13 @@
if (mNewCachedSet->getSkipCount() <=
mTunables.mRenderScheduling->maxDeferRenderAttempts) {
- ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us",
- std::chrono::duration_cast<std::chrono::microseconds>(
- estimatedRenderFinish - *renderDeadline)
- .count());
+ SFTRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us",
+ std::chrono::duration_cast<std::chrono::microseconds>(
+ estimatedRenderFinish - *renderDeadline)
+ .count());
return;
} else {
- ATRACE_NAME("DeadlinePassed: exceeded max skips");
+ SFTRACE_NAME("DeadlinePassed: exceeded max skips");
}
}
}
@@ -271,7 +270,7 @@
// was already populated with these layers, i.e. on the second and following
// calls with the same geometry.
bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::vector<CachedSet> merged;
if (mLayers.empty()) {
@@ -415,7 +414,7 @@
}
std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::vector<Run> runs;
bool isPartOfRun = false;
Run::Builder builder;
@@ -431,8 +430,8 @@
if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
auto layerFps = currentSet->getFirstLayer().getState()->getFps();
if (layerFps > 0 && layerFps <= kFpsActiveThreshold) {
- ATRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f",
- currentSet->getFirstLayer().getName().c_str(), layerFps);
+ SFTRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f",
+ currentSet->getFirstLayer().getName().c_str(), layerFps);
layerIsInactive = true;
}
}
@@ -494,7 +493,7 @@
}
void Flattener::buildCachedSets(time_point now) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mLayers.empty()) {
ALOGV("[%s] No layers found, returning", __func__);
return;
@@ -508,7 +507,7 @@
for (const CachedSet& layer : mLayers) {
// TODO (b/191997217): make it less aggressive, and sync with findCandidateRuns
if (layer.hasProtectedLayers()) {
- ATRACE_NAME("layer->hasProtectedLayers()");
+ SFTRACE_NAME("layer->hasProtectedLayers()");
return;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 5e6cade..d114ff7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -21,11 +21,11 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/properties.h>
+#include <common/trace.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
-#include <utils/Trace.h>
#include <chrono>
namespace android::compositionengine::impl::planner {
@@ -83,7 +83,7 @@
void Planner::plan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::unordered_set<LayerId> removedLayers;
removedLayers.reserve(mPreviousLayers.size());
@@ -165,7 +165,7 @@
void Planner::reportFinalPlan(
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!mPredictorEnabled) {
return;
}
@@ -204,7 +204,7 @@
void Planner::renderCachedSets(const OutputCompositionState& outputState,
std::optional<std::chrono::steady_clock::time_point> renderDeadline,
bool deviceHandlesColorTransform) {
- ATRACE_CALL();
+ SFTRACE_CALL();
mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp
new file mode 100644
index 0000000..dd04df6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include <future>
+
+#include <compositionengine/impl/HwcAsyncWorker.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+using namespace std::chrono_literals;
+
+// For the edge case tests below, how much real time should be spent trying to reproduce edge cases
+// problems in a loop.
+//
+// Larger values mean problems are more likely to be detected, at the cost of making the unit test
+// run slower.
+//
+// As we expect the tests to be run continuously, even a short loop will eventually catch
+// problems, though not necessarily from changes in the same build that introduce them.
+constexpr auto kWallTimeForEdgeCaseTests = 5ms;
+
+TEST(HwcAsyncWorker, continuousTasksEdgeCase) {
+ // Ensures that a single worker that is given multiple tasks in short succession will run them.
+
+ impl::HwcAsyncWorker worker;
+ const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+ while (std::chrono::steady_clock::now() < endTime) {
+ auto f1 = worker.send([] { return false; });
+ EXPECT_FALSE(f1.get());
+ auto f2 = worker.send([] { return true; });
+ EXPECT_TRUE(f2.get());
+ }
+}
+
+TEST(HwcAsyncWorker, constructAndDestroyEdgeCase) {
+ // Ensures that newly created HwcAsyncWorkers can be immediately destroyed.
+
+ const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+ while (std::chrono::steady_clock::now() < endTime) {
+ impl::HwcAsyncWorker worker;
+ }
+}
+
+TEST(HwcAsyncWorker, newlyCreatedRunsTasksEdgeCase) {
+ // Ensures that newly created HwcAsyncWorkers will run a task if given one immediately.
+
+ const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+ while (std::chrono::steady_clock::now() < endTime) {
+ impl::HwcAsyncWorker worker;
+ auto f = worker.send([] { return true; });
+ f.get();
+ }
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index b0b1a02..eb6e677 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -33,6 +33,7 @@
#include "DisplayHardware/HWC2.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -77,6 +78,7 @@
Error(const std::string&, bool, const std::vector<uint8_t>&));
MOCK_METHOD1(setBrightness, Error(float));
MOCK_METHOD1(setBlockingRegion, Error(const android::Region&));
+ MOCK_METHOD(Error, setLuts, (std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 629d9f2..e910c72 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -51,7 +51,8 @@
MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
- MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+ MOCK_METHOD3(allocatePhysicalDisplay,
+ void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>));
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
MOCK_METHOD(status_t, getDeviceCompositionChanges,
@@ -151,6 +152,10 @@
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
+ MOCK_METHOD(status_t, getRequestedLuts,
+ (PhysicalDisplayId,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
+ (override));
};
} // namespace mock
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index a6a9bec..f8b6c6e 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -22,7 +22,9 @@
#include "Display/DisplaySnapshot.h"
#include "DisplayHardware/HWComposer.h"
+#include <android-base/properties.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <ftl/concat.h>
#include <ftl/expected.h>
#include <log/log.h>
@@ -83,7 +85,7 @@
FTL_EXPECT(mDisplays.get(displayId).ok_or(DesiredModeAction::None)).get();
{
- ATRACE_NAME(displayPtr->concatId(__func__).c_str());
+ SFTRACE_NAME(displayPtr->concatId(__func__).c_str());
ALOGD("%s %s", displayPtr->concatId(__func__).c_str(), to_string(desiredMode).c_str());
std::scoped_lock lock(displayPtr->desiredModeLock);
@@ -204,7 +206,7 @@
return false;
}
- ATRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+ SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
return true;
}
@@ -227,8 +229,8 @@
Fps vsyncRate, Fps renderFps) {
const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
- ATRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue());
- ATRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue());
+ SFTRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue());
+ SFTRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue());
displayPtr->selectorPtr->setActiveMode(modeId, renderFps);
@@ -237,4 +239,66 @@
}
}
+void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+ const auto controllerOpt = displayPtr->selectorPtr->kernelIdleTimerController();
+ if (!controllerOpt) return;
+
+ using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
+
+ switch (displayPtr->selectorPtr->getIdleTimerAction()) {
+ case KernelIdleTimerAction::TurnOff:
+ if (displayPtr->isKernelIdleTimerEnabled) {
+ SFTRACE_INT("KernelIdleTimer", 0);
+ updateKernelIdleTimer(displayId, std::chrono::milliseconds::zero(), *controllerOpt);
+ displayPtr->isKernelIdleTimerEnabled = false;
+ }
+ break;
+ case KernelIdleTimerAction::TurnOn:
+ if (!displayPtr->isKernelIdleTimerEnabled) {
+ SFTRACE_INT("KernelIdleTimer", 1);
+ const auto timeout = displayPtr->selectorPtr->getIdleTimerTimeout();
+ updateKernelIdleTimer(displayId, timeout, *controllerOpt);
+ displayPtr->isKernelIdleTimerEnabled = true;
+ }
+ break;
+ }
+}
+
+void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId,
+ std::chrono::milliseconds timeout,
+ KernelIdleTimerController controller) {
+ switch (controller) {
+ case KernelIdleTimerController::HwcApi:
+ mComposerPtr->setIdleTimerEnabled(displayId, timeout);
+ break;
+
+ case KernelIdleTimerController::Sysprop:
+ using namespace std::string_literals;
+ base::SetProperty("graphics.display.kernel_idle_timer.enabled"s,
+ timeout > std::chrono::milliseconds::zero() ? "true"s : "false"s);
+ break;
+ }
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
+auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const
+ -> KernelIdleTimerState {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(KernelIdleTimerState())).get();
+
+ const auto desiredModeIdOpt =
+ (std::scoped_lock(displayPtr->desiredModeLock), displayPtr->desiredModeOpt)
+ .transform([](const display::DisplayModeRequest& request) {
+ return request.mode.modePtr->getId();
+ });
+
+ return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
+}
+
+#pragma clang diagnostic pop
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 258b04b..9ec603d 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -97,6 +97,17 @@
void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
EXCLUDES(mDisplayLock);
+ void updateKernelIdleTimer(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ struct KernelIdleTimerState {
+ std::optional<DisplayModeId> desiredModeIdOpt = std::nullopt;
+ bool isEnabled = false;
+ };
+
+ KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
private:
struct Display {
template <size_t N>
@@ -121,6 +132,8 @@
DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext);
bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
+
+ bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false;
};
using DisplayPtr = std::unique_ptr<Display>;
@@ -128,6 +141,10 @@
void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
REQUIRES(mDisplayLock);
+ using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
+ void updateKernelIdleTimer(PhysicalDisplayId, std::chrono::milliseconds timeout,
+ KernelIdleTimerController) REQUIRES(mDisplayLock);
+
// Set once when initializing the DisplayModeController, which the HWComposer must outlive.
HWComposer* mComposerPtr = nullptr;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 05f4da2..402a3d2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -24,6 +24,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -200,19 +201,6 @@
return mPowerMode != hal::PowerMode::OFF;
}
-nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
- const auto physicalId = getPhysicalId();
- if (!mHwComposer.isConnected(physicalId)) {
- return 0;
- }
-
- if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) {
- return *vsyncPeriodOpt;
- }
-
- return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs();
-}
-
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
return mCompositionDisplay->getState().dataspace;
}
@@ -398,7 +386,7 @@
}
void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) {
- ATRACE_CALL();
+ SFTRACE_CALL();
mHdrSdrRatio = currentHdrSdrRatio;
if (mHdrSdrRatioOverlay) {
mHdrSdrRatioOverlay->changeHdrSdrRatio(currentHdrSdrRatio);
@@ -440,7 +428,7 @@
}
void DisplayDevice::updateRefreshRateOverlayRate(Fps refreshRate, Fps renderFps, bool setByHwc) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mRefreshRateOverlay) {
if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) {
if (mRefreshRateSelector->isVrrDevice() && !mRefreshRateOverlay->isSetByHwc()) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1b8a3a8..3e3f558 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -203,8 +203,6 @@
void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio);
bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; }
- nsecs_t getVsyncPeriodFromHWC() const;
-
Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
// Round the requested refresh rate to match a divisor of the pacesetter
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 362ab9c..66237b9 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -25,9 +25,8 @@
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <common/FlagManager.h>
-#include <gui/TraceUtils.h>
+#include <common/trace.h>
#include <log/log.h>
-#include <utils/Trace.h>
#include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h>
@@ -45,6 +44,7 @@
using aidl::android::hardware::graphics::composer3::BnComposerCallback;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
+using aidl::android::hardware::graphics::composer3::Lut;
using aidl::android::hardware::graphics::composer3::PowerMode;
using aidl::android::hardware::graphics::composer3::VirtualDisplay;
@@ -60,6 +60,7 @@
using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
using AidlHdrConversionCapability =
aidl::android::hardware::graphics::common::HdrConversionCapability;
+using AidlHdcpLevels = aidl::android::hardware::drm::HdcpLevels;
using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy;
using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
@@ -223,6 +224,12 @@
return ::ndk::ScopedAStatus::ok();
}
+ ::ndk::ScopedAStatus onHdcpLevelsChanged(int64_t in_display,
+ const AidlHdcpLevels& levels) override {
+ mCallback.onComposerHalHdcpLevelsChanged(translate<Display>(in_display), levels);
+ return ::ndk::ScopedAStatus::ok();
+ }
+
private:
HWC2::ComposerCallback& mCallback;
};
@@ -677,7 +684,7 @@
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
const auto displayId = translate<int64_t>(display);
- ATRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
+ SFTRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
Error error = Error::NONE;
mMutex.lock_shared();
@@ -810,7 +817,7 @@
int32_t frameIntervalNs, uint32_t* outNumTypes,
uint32_t* outNumRequests) {
const auto displayId = translate<int64_t>(display);
- ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);
+ SFTRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);
Error error = Error::NONE;
mMutex.lock_shared();
@@ -840,7 +847,7 @@
uint32_t* outNumRequests, int* outPresentFence,
uint32_t* state) {
const auto displayId = translate<int64_t>(display);
- ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);
+ SFTRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);
Error error = Error::NONE;
mMutex.lock_shared();
@@ -1540,6 +1547,30 @@
return error;
}
+Error AidlComposer::getRequestedLuts(Display display, std::vector<DisplayLuts::LayerLut>* outLuts) {
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ *outLuts = reader->get().takeDisplayLuts(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
+}
+
+Error AidlComposer::setLayerLuts(Display display, Layer layer, std::vector<Lut>& luts) {
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerLuts(translate<int64_t>(display), translate<int64_t>(layer), luts);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
+}
+
Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) {
Error error = Error::NONE;
mMutex.lock_shared();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index ea0e53a..246223a 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -244,6 +244,13 @@
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) override;
+ Error getRequestedLuts(
+ Display display,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
+ outLuts) override;
+ Error setLayerLuts(
+ Display display, Layer layer,
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index bc067a0..7db9a94 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -40,7 +40,9 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <aidl/android/hardware/graphics/common/Transform.h>
@@ -303,6 +305,9 @@
virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) = 0;
+ virtual Error getRequestedLuts(Display display,
+ std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0;
+ virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index c77cdd4..748765a 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -31,10 +31,11 @@
#include <utils/String8.h>
#include <log/log.h>
-#include <hardware/hardware.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
+#include <hardware/hardware.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
@@ -48,10 +49,18 @@
using ui::Dataspace;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
+ const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer,
+ const ui::Size& size, const ui::Size& maxSize)
+ : ConsumerBase(producer, consumer),
+#else
FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer,
const ui::Size& size, const ui::Size& maxSize)
: ConsumerBase(consumer),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mDisplayId(displayId),
mMaxSize(maxSize),
mCurrentBufferSlot(-1),
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 2728cf6..6ca64a2 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <com_android_graphics_libgui_flags.h>
#include <compositionengine/DisplaySurface.h>
#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
@@ -40,9 +41,16 @@
class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
+ const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size,
+ const ui::Size& maxSize);
+#else
FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size,
const ui::Size& maxSize);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8c0f81e..f1fa938 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -41,6 +41,8 @@
using aidl::android::hardware::graphics::composer3::Composition;
using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::DisplayLuts;
+using aidl::android::hardware::graphics::composer3::Lut;
using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -607,6 +609,19 @@
return static_cast<Error>(error);
}
+Error Display::getRequestedLuts(std::vector<DisplayLuts::LayerLut>* outLayerLuts) {
+ std::vector<DisplayLuts::LayerLut> tmpLayerLuts;
+ const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts);
+ for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) {
+ if (layerLut.lut.pfd.get() >= 0) {
+ outLayerLuts->push_back({layerLut.layer,
+ Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()),
+ layerLut.lut.lutProperties}});
+ }
+ }
+ return static_cast<Error>(error);
+}
+
Error Display::getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) {
@@ -660,6 +675,11 @@
}
});
}
+
+void Display::setPhysicalSizeInMm(std::optional<ui::Size> size) {
+ mPhysicalSize = size;
+}
+
} // namespace impl
// Layer methods
@@ -1037,6 +1057,14 @@
return static_cast<Error>(intError);
}
+Error Layer::setLuts(std::vector<Lut>& luts) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+ const auto intError = mComposer.setLayerLuts(mDisplay->getId(), mId, luts);
+ return static_cast<Error>(intError);
+}
+
} // namespace impl
} // namespace HWC2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 5b94831..8e2aeaf 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -45,6 +45,7 @@
#include <aidl/android/hardware/graphics/composer3/Color.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
@@ -66,6 +67,7 @@
namespace hal = android::hardware::graphics::composer::hal;
+using aidl::android::hardware::drm::HdcpLevels;
using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
@@ -84,6 +86,7 @@
virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0;
virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0;
virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) = 0;
+ virtual void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) = 0;
protected:
~ComposerCallback() = default;
@@ -102,6 +105,7 @@
virtual bool isVsyncPeriodSwitchSupported() const = 0;
virtual bool hasDisplayIdleTimerCapability() const = 0;
virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
+ virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0;
[[nodiscard]] virtual hal::Error acceptChanges() = 0;
[[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
@@ -178,6 +182,9 @@
[[nodiscard]] virtual hal::Error getClientTargetProperty(
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) = 0;
+ [[nodiscard]] virtual hal::Error getRequestedLuts(
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
+ outLuts) = 0;
[[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) = 0;
@@ -261,6 +268,9 @@
hal::Error getClientTargetProperty(
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) override;
+ hal::Error getRequestedLuts(
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
+ outLuts) override;
hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) override;
@@ -276,6 +286,8 @@
bool hasDisplayIdleTimerCapability() const override;
void onLayerDestroyed(hal::HWLayerId layerId) override;
hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override;
+ void setPhysicalSizeInMm(std::optional<ui::Size> size);
+ std::optional<ui::Size> getPhysicalSizeInMm() const override { return mPhysicalSize; }
private:
void loadDisplayCapabilities();
@@ -309,6 +321,8 @@
std::optional<
std::unordered_set<aidl::android::hardware::graphics::composer3::DisplayCapability>>
mDisplayCapabilities GUARDED_BY(mDisplayCapabilitiesMutex);
+ // Physical size in mm.
+ std::optional<ui::Size> mPhysicalSize;
};
} // namespace impl
@@ -354,6 +368,8 @@
// AIDL HAL
[[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0;
[[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0;
+ [[nodiscard]] virtual hal::Error setLuts(
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) = 0;
};
namespace impl {
@@ -404,6 +420,8 @@
// AIDL HAL
hal::Error setBrightness(float brightness) override;
hal::Error setBlockingRegion(const android::Region& region) override;
+ hal::Error setLuts(
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override;
private:
// These are references to data owned by HWComposer, which will outlive
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3d285a8..d08e261 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -28,16 +28,15 @@
#include "HWComposer.h"
#include <android-base/properties.h>
+#include <common/trace.h>
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <ftl/concat.h>
-#include <gui/TraceUtils.h>
#include <log/log.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <utils/Errors.h>
-#include <utils/Trace.h>
#include "../Layer.h" // needed only for debugging
#include "../SurfaceFlingerProperties.h"
@@ -77,6 +76,7 @@
using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::DisplayConfiguration;
using namespace std::string_literals;
namespace android {
@@ -178,8 +178,8 @@
displayData.lastPresentTimestamp = timestamp;
}
- ATRACE_INT(ftl::Concat("HW_VSYNC_", displayIdOpt->value).c_str(),
- displayData.vsyncTraceToggle);
+ SFTRACE_INT(ftl::Concat("HW_VSYNC_", displayIdOpt->value).c_str(),
+ displayData.vsyncTraceToggle);
displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;
return displayIdOpt;
@@ -223,8 +223,8 @@
return true;
}
-void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
- PhysicalDisplayId displayId) {
+void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId,
+ std::optional<ui::Size> physicalSize) {
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
if (!mPrimaryHwcDisplayId) {
@@ -236,6 +236,7 @@
std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
hal::DisplayType::PHYSICAL);
newDisplay->setConnected(true);
+ newDisplay->setPhysicalSizeInMm(physicalSize);
displayData.hwcDisplay = std::move(newDisplay);
}
@@ -277,6 +278,47 @@
return getModesFromLegacyDisplayConfigs(hwcDisplayId);
}
+DisplayConfiguration::Dpi HWComposer::getEstimatedDotsPerInchFromSize(
+ uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const {
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ return {-1, -1};
+ }
+ if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+ if (const auto it = mDisplayData.find(displayId.value());
+ it != mDisplayData.end() && it->second.hwcDisplay->getPhysicalSizeInMm()) {
+ ui::Size size = it->second.hwcDisplay->getPhysicalSizeInMm().value();
+ if (hwcMode.width > 0 && hwcMode.height > 0 && size.width > 0 && size.height > 0) {
+ static constexpr float kMmPerInch = 25.4f;
+ return {hwcMode.width * kMmPerInch / size.width,
+ hwcMode.height * kMmPerInch / size.height};
+ }
+ }
+ }
+ return {-1, -1};
+}
+
+DisplayConfiguration::Dpi HWComposer::correctedDpiIfneeded(
+ DisplayConfiguration::Dpi dpi, DisplayConfiguration::Dpi estimatedDpi) const {
+ // hwc can be unreliable when it comes to dpi. A rough estimated dpi may yield better
+ // results. For instance, libdrm and bad edid may result in a dpi of {350, 290} for a
+ // 16:9 3840x2160 display, which would match a 4:3 aspect ratio.
+ // The logic here checks if hwc was able to provide some dpi, and if so if the dpi
+ // disparity between the axes is more reasonable than a rough estimate, otherwise use
+ // the estimated dpi as a corrected value.
+ if (estimatedDpi.x == -1 || estimatedDpi.y == -1) {
+ return dpi;
+ }
+ if (dpi.x == -1 || dpi.y == -1) {
+ return estimatedDpi;
+ }
+ if (std::min(dpi.x, dpi.y) != 0 && std::min(estimatedDpi.x, estimatedDpi.y) != 0 &&
+ abs(dpi.x - dpi.y) / std::min(dpi.x, dpi.y) >
+ abs(estimatedDpi.x - estimatedDpi.y) / std::min(estimatedDpi.x, estimatedDpi.y)) {
+ return estimatedDpi;
+ }
+ return dpi;
+}
+
std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations(
uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const {
std::vector<hal::DisplayConfiguration> configs;
@@ -295,9 +337,16 @@
.configGroup = config.configGroup,
.vrrConfig = config.vrrConfig};
+ const DisplayConfiguration::Dpi estimatedDPI =
+ getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
if (config.dpi) {
- hwcMode.dpiX = config.dpi->x;
- hwcMode.dpiY = config.dpi->y;
+ const DisplayConfiguration::Dpi dpi =
+ correctedDpiIfneeded(config.dpi.value(), estimatedDPI);
+ hwcMode.dpiX = dpi.x;
+ hwcMode.dpiY = dpi.y;
+ } else if (estimatedDPI.x != -1 && estimatedDPI.y != -1) {
+ hwcMode.dpiX = estimatedDPI.x;
+ hwcMode.dpiY = estimatedDPI.y;
}
if (!mEnableVrrTimeout) {
@@ -329,12 +378,14 @@
const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
- if (dpiX != -1) {
- hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f;
- }
- if (dpiY != -1) {
- hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f;
- }
+ const DisplayConfiguration::Dpi hwcDpi =
+ DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f,
+ dpiY == -1 ? dpiY : dpiY / 1000.f};
+ const DisplayConfiguration::Dpi estimatedDPI =
+ getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
+ const DisplayConfiguration::Dpi dpi = correctedDpiIfneeded(hwcDpi, estimatedDPI);
+ hwcMode.dpiX = dpi.x;
+ hwcMode.dpiY = dpi.y;
modes.push_back(hwcMode);
}
@@ -428,14 +479,14 @@
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
RETURN_IF_HWC_ERROR(error, displayId);
displayData.vsyncEnabled = enabled;
- ATRACE_INT(ftl::Concat("HW_VSYNC_ON_", displayId.value).c_str(),
- enabled == hal::Vsync::ENABLE ? 1 : 0);
+ SFTRACE_INT(ftl::Concat("HW_VSYNC_ON_", displayId.value).c_str(),
+ enabled == hal::Vsync::ENABLE ? 1 : 0);
}
status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
@@ -455,7 +506,7 @@
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
nsecs_t expectedPresentTime, Fps frameInterval,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
- ATRACE_CALL();
+ SFTRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -493,7 +544,7 @@
}();
displayData.validateWasSkipped = false;
- ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue());
+ SFTRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue());
if (canSkipValidate) {
sp<Fence> outPresentFence = Fence::NO_FENCE;
uint32_t state = UINT32_MAX;
@@ -534,6 +585,7 @@
DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+ RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX);
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
@@ -568,7 +620,7 @@
status_t HWComposer::presentAndGetReleaseFences(
HalDisplayId displayId,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) {
- ATRACE_CALL();
+ SFTRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -584,7 +636,7 @@
}
if (earliestPresentTime) {
- ATRACE_NAME("wait for earliest present time");
+ SFTRACE_NAME("wait for earliest present time");
std::this_thread::sleep_until(*earliestPresentTime);
}
@@ -897,9 +949,9 @@
status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId,
TimePoint expectedPresentTime, Fps frameInterval) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__,
- ticks<std::milli, float>(expectedPresentTime - TimePoint::now()),
- ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs())));
+ SFTRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__,
+ ticks<std::milli, float>(expectedPresentTime - TimePoint::now()),
+ ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs())));
const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(),
expectedPresentTime.ns(),
frameInterval.getPeriodNsecs());
@@ -926,6 +978,21 @@
return NO_ERROR;
}
+status_t HWComposer::getRequestedLuts(
+ PhysicalDisplayId displayId,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts);
+ if (error == hal::Error::UNSUPPORTED) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == hal::Error::BAD_PARAMETER) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
@@ -1057,6 +1124,8 @@
getDisplayIdentificationData(hwcDisplayId, &port, &data);
if (auto newInfo = parseDisplayIdentificationData(port, data)) {
info->deviceProductInfo = std::move(newInfo->deviceProductInfo);
+ info->preferredDetailedTimingDescriptor =
+ std::move(newInfo->preferredDetailedTimingDescriptor);
} else {
ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
}
@@ -1099,7 +1168,11 @@
}
if (!isConnected(info->id)) {
- allocatePhysicalDisplay(hwcDisplayId, info->id);
+ std::optional<ui::Size> size = std::nullopt;
+ if (info->preferredDetailedTimingDescriptor) {
+ size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ }
+ allocatePhysicalDisplay(hwcDisplayId, info->id, size);
}
return info;
}
@@ -1149,7 +1222,7 @@
status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId,
std::chrono::milliseconds timeout) {
- ATRACE_CALL();
+ SFTRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setIdleTimerEnabled(timeout);
if (error == hal::Error::UNSUPPORTED) {
@@ -1168,7 +1241,7 @@
}
Hwc2::AidlTransform HWComposer::getPhysicalDisplayOrientation(PhysicalDisplayId displayId) const {
- ATRACE_CALL();
+ SFTRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, Hwc2::AidlTransform::NONE);
Hwc2::AidlTransform outTransform;
const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 9368b7b..b95c619 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -52,6 +52,7 @@
#include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -133,7 +134,8 @@
// supported by the HWC can be queried in advance, but allocation may fail for other reasons.
virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0;
- virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
+ virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ std::optional<ui::Size> physicalSize) = 0;
// Attempts to create a new layer on this display
virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0;
@@ -309,6 +311,11 @@
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) = 0;
+
+ // Composer 4.0
+ virtual status_t getRequestedLuts(
+ PhysicalDisplayId,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -343,7 +350,8 @@
bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
- void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
+ void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ std::optional<ui::Size> physicalSize) override;
// Attempts to create a new layer on this display
std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override;
@@ -472,6 +480,12 @@
status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) override;
+ // Composer 4.0
+ status_t getRequestedLuts(
+ PhysicalDisplayId,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
+ override;
+
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
void dumpOverlayProperties(std::string& out) const override;
@@ -520,6 +534,13 @@
std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi
+ getEstimatedDotsPerInchFromSize(uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const;
+
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi correctedDpiIfneeded(
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi dpi,
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi estimatedDpi)
+ const;
std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId,
int32_t maxFrameIntervalNs) const;
std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index be95913..ee1e07a 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -27,11 +27,11 @@
#include <SurfaceFlingerProperties.h>
#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <android/binder_manager.h>
+#include <common/trace.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
#include <log/log.h>
-#include <utils/Trace.h>
#include "HWC2.h"
#include "Hal.h"
@@ -46,6 +46,8 @@
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using aidl::android::hardware::graphics::composer3::DimmingStage;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::DisplayLuts;
+using aidl::android::hardware::graphics::composer3::Lut;
using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -588,7 +590,7 @@
}
Error HidlComposer::presentDisplay(Display display, int* outPresentFence) {
- ATRACE_NAME("HwcPresentDisplay");
+ SFTRACE_NAME("HwcPresentDisplay");
mWriter.selectDisplay(display);
mWriter.presentDisplay();
@@ -676,7 +678,7 @@
Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
int32_t /*frameIntervalNs*/, uint32_t* outNumTypes,
uint32_t* outNumRequests) {
- ATRACE_NAME("HwcValidateDisplay");
+ SFTRACE_NAME("HwcValidateDisplay");
mWriter.selectDisplay(display);
mWriter.validateDisplay();
@@ -694,7 +696,7 @@
int32_t /*frameIntervalNs*/, uint32_t* outNumTypes,
uint32_t* outNumRequests, int* outPresentFence,
uint32_t* state) {
- ATRACE_NAME("HwcPresentOrValidateDisplay");
+ SFTRACE_NAME("HwcPresentOrValidateDisplay");
mWriter.selectDisplay(display);
mWriter.presentOrvalidateDisplay();
@@ -1408,6 +1410,14 @@
return Error::NONE;
}
+Error HidlComposer::getRequestedLuts(Display, std::vector<DisplayLuts::LayerLut>*) {
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerLuts(Display, Layer, std::vector<Lut>&) {
+ return Error::NONE;
+}
+
Error HidlComposer::setLayerBrightness(Display, Layer, float) {
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d78bfb7..701a54b 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -351,6 +351,12 @@
Hdr*) override;
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
+ Error getRequestedLuts(
+ Display,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
+ override;
+ Error setLayerLuts(Display, Layer,
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>&) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 6c1a813..334c104 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -27,9 +27,9 @@
#include <optional>
#include <android-base/properties.h>
+#include <common/trace.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
-#include <utils/Trace.h>
#include <binder/IServiceManager.h>
@@ -74,9 +74,9 @@
void traceExpensiveRendering(bool enabled) {
if (enabled) {
- ATRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
+ SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
} else {
- ATRACE_ASYNC_END("ExpensiveRendering", 0);
+ SFTRACE_ASYNC_END("ExpensiveRendering", 0);
}
}
@@ -210,8 +210,8 @@
ALOGV("Power hint session is not enabled, skip sending session hint");
return;
}
- ATRACE_CALL();
- if (sTraceHintSessionData) ATRACE_INT("Session hint", static_cast<int>(hint));
+ SFTRACE_CALL();
+ if (sTraceHintSessionData) SFTRACE_INT("Session hint", static_cast<int>(hint));
{
std::scoped_lock lock(mHintSessionMutex);
if (!ensurePowerHintSessionRunning()) {
@@ -295,10 +295,10 @@
ALOGV("Power hint session is not enabled, skipping target update");
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
{
mTargetDuration = targetDuration;
- if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
+ if (sTraceHintSessionData) SFTRACE_INT64("Time target", targetDuration.ns());
if (targetDuration == mLastTargetDurationSent) return;
std::scoped_lock lock(mHintSessionMutex);
if (!ensurePowerHintSessionRunning()) {
@@ -324,7 +324,7 @@
ALOGV("Actual work duration power hint cannot be sent, skipping");
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
std::optional<WorkDuration> actualDuration = estimateWorkDuration();
if (!actualDuration.has_value() || actualDuration->durationNanos < 0) {
ALOGV("Failed to send actual work duration, skipping");
@@ -332,16 +332,16 @@
}
actualDuration->durationNanos += sTargetSafetyMargin.ns();
if (sTraceHintSessionData) {
- ATRACE_INT64("Measured duration", actualDuration->durationNanos);
- ATRACE_INT64("Target error term", actualDuration->durationNanos - mTargetDuration.ns());
- ATRACE_INT64("Reported duration", actualDuration->durationNanos);
+ SFTRACE_INT64("Measured duration", actualDuration->durationNanos);
+ SFTRACE_INT64("Target error term", actualDuration->durationNanos - mTargetDuration.ns());
+ SFTRACE_INT64("Reported duration", actualDuration->durationNanos);
if (supportsGpuReporting()) {
- ATRACE_INT64("Reported cpu duration", actualDuration->cpuDurationNanos);
- ATRACE_INT64("Reported gpu duration", actualDuration->gpuDurationNanos);
+ SFTRACE_INT64("Reported cpu duration", actualDuration->cpuDurationNanos);
+ SFTRACE_INT64("Reported gpu duration", actualDuration->gpuDurationNanos);
}
- ATRACE_INT64("Reported target", mLastTargetDurationSent.ns());
- ATRACE_INT64("Reported target error term",
- actualDuration->durationNanos - mLastTargetDurationSent.ns());
+ SFTRACE_INT64("Reported target", mLastTargetDurationSent.ns());
+ SFTRACE_INT64("Reported target error term",
+ actualDuration->durationNanos - mLastTargetDurationSent.ns());
}
ALOGV("Sending actual work duration of: %" PRId64 " with cpu: %" PRId64 " and gpu: %" PRId64
@@ -664,9 +664,9 @@
.gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0,
};
if (sTraceHintSessionData) {
- ATRACE_INT64("Idle duration", idleDuration.ns());
- ATRACE_INT64("Total duration", totalDuration.ns());
- ATRACE_INT64("Flinger duration", flingerDuration.ns());
+ SFTRACE_INT64("Idle duration", idleDuration.ns());
+ SFTRACE_INT64("Total duration", totalDuration.ns());
+ SFTRACE_INT64("Flinger duration", flingerDuration.ns());
}
return std::make_optional(duration);
}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index bc4a41b..1076b2b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -300,7 +300,7 @@
bool mSessionConfigSupported = true;
bool mFirstConfigSupportCheck = true;
- // Whether we should emit ATRACE_INT data for hint sessions
+ // Whether we should emit SFTRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
// Default target duration for the hint session
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4b5a68c..384f7b2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -22,6 +22,7 @@
#include <cinttypes>
+#include <com_android_graphics_libgui_flags.h>
#include <ftl/enum.h>
#include <ftl/flags.h>
#include <gui/BufferItem.h>
@@ -51,7 +52,11 @@
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
const std::string& name)
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ : ConsumerBase(bqProducer, bqConsumer),
+#else
: ConsumerBase(bqConsumer),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mHwc(hwc),
mDisplayId(displayId),
mDisplayName(name),
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 2596a25..47b811b 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -22,20 +22,24 @@
#include <android-base/stringprintf.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <utils/Log.h>
-#include <utils/Trace.h>
#include <chrono>
#include <cinttypes>
#include <numeric>
#include <unordered_set>
+#include "../Jank/JankTracker.h"
+
namespace android::frametimeline {
using base::StringAppendF;
using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
+namespace {
+
void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
StringAppendF(&result, "%s", indent.c_str());
@@ -317,6 +321,16 @@
return minTime;
}
+bool shouldTraceForDataSource(const FrameTimelineDataSource::TraceContext& ctx, nsecs_t timestamp) {
+ if (auto ds = ctx.GetDataSourceLocked(); ds && ds->getStartTime() > timestamp) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
int64_t TraceCookieCounter::getCookieForTracing() {
return ++mTraceCookie;
}
@@ -357,7 +371,11 @@
void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) {
std::scoped_lock lock(mMutex);
- mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+ if (CC_UNLIKELY(acquireFenceTime == Fence::SIGNAL_TIME_PENDING)) {
+ mActuals.endTime = mActualQueueTime;
+ } else {
+ mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+ }
}
void SurfaceFrame::setDropTime(nsecs_t dropTime) {
@@ -685,6 +703,30 @@
mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
mGameMode, mJankType, displayDeadlineDelta,
displayPresentDelta, deadlineDelta});
+
+ gui::JankData jd;
+ jd.frameVsyncId = mToken;
+ jd.jankType = mJankType;
+ jd.frameIntervalNs =
+ (mRenderRate ? *mRenderRate : mDisplayFrameRenderRate).getPeriodNsecs();
+
+ if (mPredictionState == PredictionState::Valid) {
+ jd.scheduledAppFrameTimeNs = mPredictions.endTime - mPredictions.startTime;
+
+ // Using expected start, rather than actual, to measure the entire frame time. That is
+ // if the application starts the frame later than scheduled, include that delay in the
+ // frame time, as it usually means main thread being busy with non-rendering work.
+ if (mPresentState == PresentState::Dropped) {
+ jd.actualAppFrameTimeNs = mDropTime - mPredictions.startTime;
+ } else {
+ jd.actualAppFrameTimeNs = mActuals.endTime - mPredictions.startTime;
+ }
+ } else {
+ jd.scheduledAppFrameTimeNs = 0;
+ jd.actualAppFrameTimeNs = 0;
+ }
+
+ JankTracker::onJankData(mLayerId, jd);
}
}
@@ -696,15 +738,24 @@
classifyJankLocked(JankType::None, refreshRate, displayFrameRenderRate, nullptr);
}
-void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
+void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Expected timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = mPredictions.startTime;
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
std::scoped_lock lock(mMutex);
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime + monoBootOffset));
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start();
@@ -718,42 +769,54 @@
expectedSurfaceFrameStartEvent->set_layer_name(mDebugName);
});
- // Expected timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset));
+ if (traced) {
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
- expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
- });
+ expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+ }
}
-void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
+void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Actual timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = [&]() {
+ std::scoped_lock lock(mMutex);
+ // Actual start time is not yet available, so use expected start instead
+ if (mPredictionState == PredictionState::Expired) {
+ // If prediction is expired, we can't use the predicted start time. Instead, just
+ // use a start time a little earlier than the end time so that we have some info
+ // about this frame in the trace.
+ nsecs_t endTime =
+ (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
+ return endTime - kPredictionExpiredStartTimeDelta;
+ }
+
+ return mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime;
+ }();
+
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
std::scoped_lock lock(mMutex);
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- // Actual start time is not yet available, so use expected start instead
- if (mPredictionState == PredictionState::Expired) {
- // If prediction is expired, we can't use the predicted start time. Instead, just use a
- // start time a little earlier than the end time so that we have some info about this
- // frame in the trace.
- nsecs_t endTime =
- (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
- const auto timestamp = endTime - kPredictionExpiredStartTimeDelta;
- packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
- } else {
- const auto timestamp =
- mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime;
- packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
- }
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start();
@@ -782,28 +845,31 @@
actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType));
});
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- if (mPresentState == PresentState::Dropped) {
- packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset));
- } else {
- packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset));
- }
+ if (traced) {
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ if (mPresentState == PresentState::Dropped) {
+ packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset));
+ } else {
+ packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset));
+ }
- auto* event = packet->set_frame_timeline_event();
- auto* actualSurfaceFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualSurfaceFrameEndEvent = event->set_frame_end();
- actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
- });
+ actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
}
/**
* TODO(b/178637512): add inputEventId to the perfetto trace.
*/
-void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
+void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID ||
displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
// No packets can be traced with a missing token.
@@ -812,15 +878,15 @@
if (getPredictionState() != PredictionState::Expired) {
// Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in
// a trace.
- tracePredictions(displayFrameToken, monoBootOffset);
+ tracePredictions(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts);
}
- traceActuals(displayFrameToken, monoBootOffset);
+ traceActuals(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts);
}
namespace impl {
int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::scoped_lock lock(mMutex);
while (mPredictions.size() >= kMaxTokens) {
mPredictions.erase(mPredictions.begin());
@@ -840,8 +906,12 @@
}
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds, bool useBootTimeClock)
+ JankClassificationThresholds thresholds, bool useBootTimeClock,
+ bool filterFramesBeforeTraceStarts)
: mUseBootTimeClock(useBootTimeClock),
+ mFilterFramesBeforeTraceStarts(
+ FlagManager::getInstance().filter_frames_before_trace_starts() &&
+ filterFramesBeforeTraceStarts),
mMaxDisplayFrames(kDefaultMaxDisplayFrames),
mTimeStats(std::move(timeStats)),
mSurfaceFlingerPid(surfaceFlingerPid),
@@ -866,7 +936,7 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
std::string layerName, std::string debugName, bool isBuffer, GameMode gameMode) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
@@ -902,14 +972,14 @@
}
void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::scoped_lock lock(mMutex);
mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
}
void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate,
Fps renderRate) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::scoped_lock lock(mMutex);
mCurrentDisplayFrame->onSfWakeUp(token, refreshRate, renderRate,
mTokenManager.getPredictionsForToken(token), wakeUpTime);
@@ -918,7 +988,7 @@
void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::scoped_lock lock(mMutex);
mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
mCurrentDisplayFrame->setGpuFence(gpuFence);
@@ -928,7 +998,7 @@
}
void FrameTimeline::onCommitNotComposited() {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::scoped_lock lock(mMutex);
mCurrentDisplayFrame->onCommitNotComposited();
mCurrentDisplayFrame.reset();
@@ -1124,16 +1194,23 @@
}
}
-void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid,
- nsecs_t monoBootOffset) const {
+void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Expected timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = mSurfaceFlingerPredictions.startTime;
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime + monoBootOffset));
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start();
@@ -1144,22 +1221,25 @@
expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
});
- // Expected timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset));
+ if (traced) {
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(
+ static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* expectedDisplayFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedDisplayFrameEndEvent = event->set_frame_end();
- expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
- });
+ expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+ }
}
void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousPredictionPresentTime) const {
+ nsecs_t previousPredictionPresentTime,
+ bool filterFramesBeforeTraceStarts) const {
nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0;
const constexpr float kThresh = 0.5f;
const constexpr float kRange = 1.5f;
@@ -1175,7 +1255,7 @@
(static_cast<float>(previousPredictionPresentTime) +
kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
// sf skipped frame is not considered if app is self janked
- !surfaceFrame->isSelfJanky()) {
+ surfaceFrame->getJankType() != JankType::None && !surfaceFrame->isSelfJanky()) {
skippedFrameStartTime = surfaceFrame->getPredictions().endTime;
skippedFramePresentTime = surfaceFrame->getPredictions().presentTime;
break;
@@ -1185,9 +1265,17 @@
// add slice
if (skippedFrameStartTime != 0 && skippedFramePresentTime != 0) {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Actual timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ if (filterFramesBeforeTraceStarts &&
+ !shouldTraceForDataSource(ctx, skippedFrameStartTime)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
packet->set_timestamp(static_cast<uint64_t>(skippedFrameStartTime + monoBootOffset));
@@ -1208,30 +1296,40 @@
actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None));
});
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset));
+ if (traced) {
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(
+ static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* actualDisplayFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
- actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
- });
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
}
}
-void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid,
- nsecs_t monoBootOffset) const {
+void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Actual timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = mSurfaceFlingerActuals.startTime;
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerActuals.startTime + monoBootOffset));
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
@@ -1250,22 +1348,25 @@
actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType));
});
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset));
+ if (traced) {
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(
+ static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* actualDisplayFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
- actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
- });
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
}
nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousPredictionPresentTime) const {
+ nsecs_t previousPredictionPresentTime,
+ bool filterFramesBeforeTraceStarts) const {
if (mSurfaceFrames.empty()) {
// We don't want to trace display frames without any surface frames updates as this cannot
// be janky
@@ -1281,16 +1382,17 @@
if (mPredictionState == PredictionState::Valid) {
// Expired and unknown predictions have zeroed timestamps. This cannot be used in any
// meaningful way in a trace.
- tracePredictions(surfaceFlingerPid, monoBootOffset);
+ tracePredictions(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts);
}
- traceActuals(surfaceFlingerPid, monoBootOffset);
+ traceActuals(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts);
for (auto& surfaceFrame : mSurfaceFrames) {
- surfaceFrame->trace(mToken, monoBootOffset);
+ surfaceFrame->trace(mToken, monoBootOffset, filterFramesBeforeTraceStarts);
}
if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) {
- addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime);
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime,
+ filterFramesBeforeTraceStarts);
}
return mSurfaceFlingerPredictions.presentTime;
}
@@ -1384,8 +1486,9 @@
const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
- mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
- mPreviousPredictionPresentTime);
+ mPreviousPredictionPresentTime =
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
@@ -1401,8 +1504,9 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
- mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
- mPreviousPredictionPresentTime);
+ mPreviousPredictionPresentTime =
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
mPreviousActualPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -1507,7 +1611,7 @@
}
void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::unordered_map<std::string, bool> argsMap;
for (size_t i = 0; i < args.size(); i++) {
argsMap[std::string(String8(args[i]).c_str())] = true;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 94cfcb4..cffb61e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -214,7 +214,8 @@
// enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
// DisplayFrame at the trace processor side. monoBootOffset is the difference
// between SYSTEM_TIME_BOOTTIME and SYSTEM_TIME_MONOTONIC.
- void trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
+ void trace(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
// Getter functions used only by FrameTimelineTests and SurfaceFrame internally
TimelineItem getActuals() const;
@@ -234,8 +235,10 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
private:
- void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
- void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
+ void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
+ void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
Fps displayFrameRenderRate, nsecs_t* outDeadlineDelta) REQUIRES(mMutex);
@@ -367,9 +370,15 @@
class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
- void OnSetup(const SetupArgs&) override{};
- void OnStart(const StartArgs&) override{};
- void OnStop(const StopArgs&) override{};
+ public:
+ nsecs_t getStartTime() const { return mTraceStartTime; }
+
+ private:
+ void OnSetup(const SetupArgs&) override {};
+ void OnStart(const StartArgs&) override { mTraceStartTime = systemTime(); };
+ void OnStop(const StopArgs&) override {};
+
+ nsecs_t mTraceStartTime = 0;
};
/*
@@ -390,7 +399,8 @@
// is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME
// and SYSTEM_TIME_MONOTONIC.
nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousPredictionPresentTime) const;
+ nsecs_t previousPredictionPresentTime,
+ bool filterFramesBeforeTraceStarts) const;
// Sets the token, vsyncPeriod, predictions and SF start time.
void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
@@ -424,10 +434,13 @@
private:
void dump(std::string& result, nsecs_t baseTime) const;
- void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
- void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
+ void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
+ void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
void addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousActualPresentTime) const;
+ nsecs_t previousActualPresentTime,
+ bool filterFramesBeforeTraceStarts) const;
void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
nsecs_t previousPresentTime);
@@ -471,7 +484,8 @@
};
FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true);
+ JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true,
+ bool filterFramesBeforeTraceStarts = true);
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
@@ -516,6 +530,7 @@
TraceCookieCounter mTraceCookieCounter;
mutable std::mutex mMutex;
const bool mUseBootTimeClock;
+ const bool mFilterFramesBeforeTraceStarts;
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 39a6b77..d709530 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -18,6 +18,8 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
+#include <android-base/logging.h>
+
#include "LayerHierarchy.h"
#include "LayerLog.h"
#include "SwapErase.h"
@@ -413,11 +415,11 @@
void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) {
if (!mInitialized) {
- ATRACE_NAME("LayerHierarchyBuilder:init");
+ SFTRACE_NAME("LayerHierarchyBuilder:init");
init(layerLifecycleManager.getLayers());
} else if (layerLifecycleManager.getGlobalChanges().test(
RequestedLayerState::Changes::Hierarchy)) {
- ATRACE_NAME("LayerHierarchyBuilder:update");
+ SFTRACE_NAME("LayerHierarchyBuilder:update");
doUpdate(layerLifecycleManager.getLayers(), layerLifecycleManager.getDestroyedLayers());
} else {
return; // nothing to do
@@ -426,7 +428,7 @@
uint32_t invalidRelativeRoot;
bool hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot);
while (hasRelZLoop) {
- ATRACE_NAME("FixRelZLoop");
+ SFTRACE_NAME("FixRelZLoop");
TransactionTraceWriter::getInstance().invoke("relz_loop_detected",
/*overwrite=*/false);
layerLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
@@ -485,6 +487,55 @@
return it->second;
}
+void LayerHierarchyBuilder::logSampledChildren(const LayerHierarchy& hierarchy) const {
+ LOG(ERROR) << "Dumping random sampling of child layers.";
+ int sampleSize = static_cast<int>(hierarchy.mChildren.size() / 100 + 1);
+ for (const auto& [child, variant] : hierarchy.mChildren) {
+ if (rand() % sampleSize == 0) {
+ LOG(ERROR) << "Child Layer: " << *(child->mLayer);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::dumpLayerSample(const LayerHierarchy& root) const {
+ LOG(ERROR) << "Dumping layer keeping > 20 children alive:";
+ // If mLayer is nullptr, it will be skipped while traversing.
+ if (!root.mLayer && root.mChildren.size() > 20) {
+ LOG(ERROR) << "ROOT has " << root.mChildren.size() << " children";
+ logSampledChildren(root);
+ }
+ root.traverse([&](const LayerHierarchy& hierarchy, const auto&) -> bool {
+ if (hierarchy.mChildren.size() <= 20) {
+ return true;
+ }
+ // mLayer is ensured to be non-null. See LayerHierarchy::traverse.
+ const auto* layer = hierarchy.mLayer;
+ const auto childrenCount = hierarchy.mChildren.size();
+ LOG(ERROR) << "Layer " << *layer << " has " << childrenCount << " children";
+
+ const auto* parent = hierarchy.mParent;
+ while (parent != nullptr) {
+ if (!parent->mLayer) break;
+ LOG(ERROR) << "Parent Layer: " << *(parent->mLayer);
+ parent = parent->mParent;
+ }
+
+ logSampledChildren(hierarchy);
+ // Stop traversing.
+ return false;
+ });
+ LOG(ERROR) << "Dumping random sampled layers.";
+ size_t numLayers = 0;
+ root.traverse([&](const LayerHierarchy& hierarchy, const auto&) -> bool {
+ if (hierarchy.mLayer) numLayers++;
+ if ((rand() % 20 == 13) && hierarchy.mLayer) {
+ LOG(ERROR) << "Layer: " << *(hierarchy.mLayer);
+ }
+ return true;
+ });
+ LOG(ERROR) << "Total layer count: " << numLayers;
+}
+
const LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT =
{.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index d023f9e..47d0041 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -211,8 +211,11 @@
const LayerHierarchy& getHierarchy() const;
const LayerHierarchy& getOffscreenHierarchy() const;
std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+ void dumpLayerSample(const LayerHierarchy& layerHierarchy) const;
private:
+ void logSampledChildren(const LayerHierarchy& hierarchy) const;
+
void onLayerAdded(RequestedLayerState* layer);
void attachToParent(LayerHierarchy*);
void detachFromParent(LayerHierarchy*);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 70e3c64..e5f6b7b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -322,6 +322,10 @@
<< touchableRegion.bottom << "," << touchableRegion.right << "}"
<< "}";
}
+
+ if (obj.edgeExtensionEffect.hasEffect()) {
+ out << obj.edgeExtensionEffect;
+ }
return out;
}
@@ -492,8 +496,10 @@
requested.what &
(layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) {
- forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect();
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged |
+ layer_state_t::eEdgeExtensionChanged)) {
+ forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect() ||
+ edgeExtensionEffect.hasEffect();
}
if (forceUpdate ||
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ca53a0d..ee605b7 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -23,8 +23,8 @@
#include <optional>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <ftl/small_map.h>
-#include <gui/TraceUtils.h>
#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
@@ -279,24 +279,6 @@
snapshot.getDebugString().c_str());
}
-bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
- if (requested.potentialCursor) {
- return false;
- }
-
- if (snapshot.inputInfo.token != nullptr) {
- return true;
- }
-
- if (snapshot.hasBufferOrSidebandStream()) {
- return true;
- }
-
- return requested.windowInfoHandle &&
- requested.windowInfoHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
-}
-
void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshotBuilder::Args& args) {
snapshot.metadata.clear();
@@ -317,6 +299,18 @@
}
}
+void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+ const LayerSnapshotBuilder::Args& args,
+ const LayerSnapshot& parentSnapshot) {
+ snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) ? requested.gameMode
+ : parentSnapshot.gameMode;
+ updateMetadata(snapshot, requested, args);
+ if (args.includeMetadata) {
+ snapshot.layerMetadata = parentSnapshot.layerMetadata;
+ snapshot.layerMetadata.merge(requested.metadata);
+ }
+}
+
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
snapshot.clientChanges = 0;
@@ -352,6 +346,7 @@
snapshot.geomLayerBounds = getMaxDisplayBounds({});
snapshot.roundedCorner = RoundedCornerState();
snapshot.stretchEffect = {};
+ snapshot.edgeExtensionEffect = {};
snapshot.outputFilter.layerStack = ui::DEFAULT_LAYER_STACK;
snapshot.outputFilter.toInternalDisplay = false;
snapshot.isSecure = false;
@@ -387,7 +382,7 @@
// There are only content changes which do not require any child layer snapshots to be updated.
ALOGV("%s", __func__);
- ATRACE_NAME("FastPath");
+ SFTRACE_NAME("FastPath");
uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
if (forceUpdate || args.displayChanges) {
@@ -421,7 +416,7 @@
}
void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
- ATRACE_NAME("UpdateSnapshots");
+ SFTRACE_NAME("UpdateSnapshots");
LayerSnapshot rootSnapshot = args.rootSnapshot;
if (args.parentCrop) {
rootSnapshot.geomLayerBounds = *args.parentCrop;
@@ -548,6 +543,7 @@
updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
}
+ bool childHasValidFrameRate = false;
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
childHierarchy->getLayer()->id,
@@ -555,7 +551,8 @@
const LayerSnapshot& childSnapshot =
updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
depth + 1);
- updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, args);
+ updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(),
+ args, &childHasValidFrameRate);
}
return *snapshot;
@@ -662,9 +659,10 @@
}
}
-void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
- const LayerSnapshot& childSnapshot,
- const Args& args) {
+void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(
+ LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
+ const RequestedLayerState& /* requestedChildState */, const Args& args,
+ bool* outChildHasValidFrameRate) {
if (args.forceUpdate == ForceUpdateFlags::NONE &&
!args.layerLifecycleManager.getGlobalChanges().any(
RequestedLayerState::Changes::Hierarchy) &&
@@ -674,7 +672,7 @@
}
using FrameRateCompatibility = scheduler::FrameRateCompatibility;
- if (snapshot.frameRate.isValid()) {
+ if (snapshot.inheritedFrameRate.isValid() || *outChildHasValidFrameRate) {
// we already have a valid framerate.
return;
}
@@ -691,13 +689,18 @@
const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.vote.rate.isValid() &&
childSnapshot.frameRate.vote.type == FrameRateCompatibility::Exact;
- bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+ *outChildHasValidFrameRate |= layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
layerVotedWithCategory || layerVotedWithExactCompatibility;
// If we don't have a valid frame rate, but the children do, we set this
// layer as NoVote to allow the children to control the refresh rate
- if (childHasValidFrameRate) {
- snapshot.frameRate = scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ static const auto noVote =
+ scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ if (*outChildHasValidFrameRate) {
+ snapshot.frameRate = noVote;
+ snapshot.changes |= RequestedLayerState::Changes::FrameRate;
+ } else if (snapshot.frameRate != snapshot.inheritedFrameRate) {
+ snapshot.frameRate = snapshot.inheritedFrameRate;
snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
}
@@ -762,6 +765,12 @@
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
+ if (forceUpdate ||
+ (args.includeMetadata &&
+ snapshot.changes.any(RequestedLayerState::Changes::Metadata |
+ RequestedLayerState::Changes::Geometry))) {
+ updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot);
+ }
return;
}
@@ -791,6 +800,32 @@
: parentSnapshot.stretchEffect;
}
+ if (forceUpdate ||
+ (snapshot.clientChanges | parentSnapshot.clientChanges) &
+ layer_state_t::eEdgeExtensionChanged) {
+ if (requested.edgeExtensionParameters.extendLeft ||
+ requested.edgeExtensionParameters.extendRight ||
+ requested.edgeExtensionParameters.extendTop ||
+ requested.edgeExtensionParameters.extendBottom) {
+ // This is the root layer to which the extension is applied
+ snapshot.edgeExtensionEffect =
+ EdgeExtensionEffect(requested.edgeExtensionParameters.extendLeft,
+ requested.edgeExtensionParameters.extendRight,
+ requested.edgeExtensionParameters.extendTop,
+ requested.edgeExtensionParameters.extendBottom);
+ } else if (parentSnapshot.clientChanges & layer_state_t::eEdgeExtensionChanged) {
+ // Extension is inherited
+ snapshot.edgeExtensionEffect = parentSnapshot.edgeExtensionEffect;
+ } else {
+ // There is no edge extension
+ snapshot.edgeExtensionEffect.reset();
+ }
+ if (snapshot.edgeExtensionEffect.hasEffect()) {
+ snapshot.clientChanges |= layer_state_t::eEdgeExtensionChanged;
+ snapshot.changes |= RequestedLayerState::Changes::Geometry;
+ }
+ }
+
if (forceUpdate || snapshot.clientChanges & layer_state_t::eColorTransformChanged) {
if (!parentSnapshot.colorTransformIsIdentity) {
snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform;
@@ -801,15 +836,10 @@
}
}
- if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
- snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
- ? requested.gameMode
- : parentSnapshot.gameMode;
- updateMetadata(snapshot, requested, args);
- if (args.includeMetadata) {
- snapshot.layerMetadata = parentSnapshot.layerMetadata;
- snapshot.layerMetadata.merge(requested.metadata);
- }
+ if (forceUpdate ||
+ snapshot.changes.any(RequestedLayerState::Changes::Metadata |
+ RequestedLayerState::Changes::Hierarchy)) {
+ updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot);
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged ||
@@ -886,6 +916,10 @@
updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
}
+ if (snapshot.edgeExtensionEffect.hasEffect()) {
+ updateBoundsForEdgeExtension(snapshot);
+ }
+
if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::BufferUsageFlags)) {
@@ -904,8 +938,8 @@
}
// computed snapshot properties
- snapshot.forceClientComposition =
- snapshot.shadowSettings.length > 0 || snapshot.stretchEffect.hasEffect();
+ snapshot.forceClientComposition = snapshot.shadowSettings.length > 0 ||
+ snapshot.stretchEffect.hasEffect() || snapshot.edgeExtensionEffect.hasEffect();
snapshot.contentOpaque = snapshot.isContentOpaque();
snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
@@ -960,6 +994,31 @@
}
}
+/**
+ * According to the edges that we are requested to extend, we increase the bounds to the maximum
+ * extension allowed by the crop (parent crop + requested crop). The animation that called
+ * Transition#setEdgeExtensionEffect is in charge of setting the requested crop.
+ * @param snapshot
+ */
+void LayerSnapshotBuilder::updateBoundsForEdgeExtension(LayerSnapshot& snapshot) {
+ EdgeExtensionEffect& effect = snapshot.edgeExtensionEffect;
+
+ if (effect.extendsEdge(LEFT)) {
+ snapshot.geomLayerBounds.left = snapshot.geomLayerCrop.left;
+ }
+ if (effect.extendsEdge(RIGHT)) {
+ snapshot.geomLayerBounds.right = snapshot.geomLayerCrop.right;
+ }
+ if (effect.extendsEdge(TOP)) {
+ snapshot.geomLayerBounds.top = snapshot.geomLayerCrop.top;
+ }
+ if (effect.extendsEdge(BOTTOM)) {
+ snapshot.geomLayerBounds.bottom = snapshot.geomLayerCrop.bottom;
+ }
+
+ snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
+}
+
void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
@@ -999,11 +1058,12 @@
FloatRect parentBounds = parentSnapshot.geomLayerBounds;
parentBounds = snapshot.localTransform.inverse().transform(parentBounds);
snapshot.geomLayerBounds =
- (requested.externalTexture) ? snapshot.bufferSize.toFloatRect() : parentBounds;
+ requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds;
+ snapshot.geomLayerCrop = parentBounds;
if (!requested.crop.isEmpty()) {
- snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(requested.crop.toFloatRect());
+ snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect());
}
- snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds);
+ snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop);
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
const Rect geomLayerBoundsWithoutTransparentRegion =
RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
@@ -1084,7 +1144,7 @@
}
updateVisibility(snapshot, snapshot.isVisible);
- if (!needsInputInfo(snapshot, requested)) {
+ if (!requested.needsInputInfo()) {
return;
}
@@ -1094,7 +1154,7 @@
bool noValidDisplay = !displayInfoOpt.has_value();
auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
- if (!requested.windowInfoHandle) {
+ if (!requested.hasInputInfo()) {
snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL;
}
fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
@@ -1178,6 +1238,21 @@
}
}
+void LayerSnapshotBuilder::forEachSnapshot(const Visitor& visitor,
+ const ConstPredicate& predicate) {
+ for (int i = 0; i < mNumInterestingSnapshots; i++) {
+ std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i);
+ if (!predicate(*snapshot)) continue;
+ visitor(snapshot);
+ }
+}
+
+void LayerSnapshotBuilder::forEachSnapshot(const ConstVisitor& visitor) const {
+ for (auto& snapshot : mSnapshots) {
+ visitor(*snapshot);
+ }
+}
+
void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const {
for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 1cec018..486cb33 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -86,6 +86,14 @@
// Visit each visible snapshot in z-order and move the snapshot if needed
void forEachVisibleSnapshot(const Visitor& visitor);
+ typedef std::function<bool(const LayerSnapshot& snapshot)> ConstPredicate;
+ // Visit each snapshot that satisfies the predicate and move the snapshot if needed with visible
+ // snapshots in z-order
+ void forEachSnapshot(const Visitor& visitor, const ConstPredicate& predicate);
+
+ // Visit each snapshot
+ void forEachSnapshot(const ConstVisitor& visitor) const;
+
// Visit each snapshot interesting to input reverse z-order
void forEachInputSnapshot(const ConstVisitor& visitor) const;
@@ -108,6 +116,10 @@
static void resetRelativeState(LayerSnapshot& snapshot);
static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, const Args& args);
+ static bool extensionEdgeSharedWithParent(LayerSnapshot& snapshot,
+ const RequestedLayerState& requested,
+ const LayerSnapshot& parentSnapshot);
+ static void updateBoundsForEdgeExtension(LayerSnapshot& snapshot);
void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
@@ -121,7 +133,9 @@
const RequestedLayerState& layer,
const LayerSnapshot& parentSnapshot);
void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
- const LayerSnapshot& childSnapshot, const Args& args);
+ const LayerSnapshot& childSnapshot,
+ const RequestedLayerState& requestedCHildState,
+ const Args& args, bool* outChildHasValidFrameRate);
void updateTouchableRegionCrop(const Args& args);
std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 3e8d740..5734ccf 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -19,7 +19,7 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
-#include <gui/TraceUtils.h>
+#include <common/trace.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <sys/types.h>
@@ -62,6 +62,8 @@
metadata.merge(args.metadata);
changes |= RequestedLayerState::Changes::Metadata;
handleAlive = true;
+ // TODO: b/305254099 remove once we don't pass invisible windows to input
+ windowInfoHandle = nullptr;
if (parentId != UNASSIGNED_LAYER_ID) {
canBeRoot = false;
}
@@ -328,6 +330,7 @@
changes |= RequestedLayerState::Changes::GameMode;
}
}
+ changes |= RequestedLayerState::Changes::Metadata;
}
if (clientState.what & layer_state_t::eFrameRateChanged) {
const auto compatibility =
@@ -552,6 +555,24 @@
windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
+bool RequestedLayerState::needsInputInfo() const {
+ if (potentialCursor) {
+ return false;
+ }
+
+ if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ return true;
+ }
+
+ if (!windowInfoHandle) {
+ return false;
+ }
+
+ const auto windowInfo = windowInfoHandle->getInfo();
+ return windowInfo->token != nullptr ||
+ windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
bool RequestedLayerState::hasBlur() const {
return backgroundBlurRadius > 0 || blurRegions.size() > 0;
}
@@ -580,8 +601,8 @@
bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
static constexpr uint64_t requiredFlags = layer_state_t::eBufferChanged;
if ((s.what & requiredFlags) != requiredFlags) {
- ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
- (s.what | requiredFlags) & ~s.what);
+ SFTRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+ (s.what | requiredFlags) & ~s.what);
return false;
}
@@ -593,8 +614,8 @@
? 0
: (layer_state_t::eAutoRefreshChanged | layer_state_t::eFlagsChanged));
if (s.what & deniedFlags) {
- ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
- s.what & deniedFlags);
+ SFTRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
+ s.what & deniedFlags);
return false;
}
@@ -608,15 +629,16 @@
layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged |
layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
- layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
- layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged |
+ layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
+ layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
+ layer_state_t::eExtendedRangeBrightnessChanged |
layer_state_t::eDesiredHdrHeadroomChanged |
(FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
? layer_state_t::eFlagsChanged
: 0);
if (changedFlags & deniedChanges) {
- ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
- changedFlags & deniedChanges);
+ SFTRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
+ changedFlags & deniedChanges);
return false;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 48b9640..1d96dff 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -87,6 +87,7 @@
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
+ bool needsInputInfo() const;
bool hasBlur() const;
bool hasFrameUpdate() const;
bool hasReadyFrame() const;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index d3d9509..a1e8213 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -19,9 +19,9 @@
#define LOG_TAG "SurfaceFlinger"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <common/trace.h>
#include <cutils/trace.h>
#include <utils/Log.h>
-#include <utils/Trace.h>
#include "FrontEnd/LayerLog.h"
#include "TransactionHandler.h"
@@ -31,7 +31,7 @@
void TransactionHandler::queueTransaction(TransactionState&& state) {
mLocklessTransactionQueue.push(std::move(state));
mPendingTransactionCount.fetch_add(1);
- ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+ SFTRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
}
void TransactionHandler::collectTransactions() {
@@ -71,7 +71,7 @@
applyUnsignaledBufferTransaction(transactions, flushState);
mPendingTransactionCount.fetch_sub(transactions.size());
- ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+ SFTRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
return transactions;
}
@@ -83,7 +83,7 @@
// only apply an unsignaled buffer transaction if it's the first one
if (!transactions.empty()) {
- ATRACE_NAME("fence unsignaled");
+ SFTRACE_NAME("fence unsignaled");
return;
}
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
index e5cca8f..4af27ab 100644
--- a/services/surfaceflinger/FrontEnd/Update.h
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -22,28 +22,13 @@
#include "RequestedLayerState.h"
#include "TransactionState.h"
-namespace android {
-struct LayerCreatedState {
- LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
- : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
- wp<Layer> layer;
- // Indicates the initial parent of the created layer, only used for creating layer in
- // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
- wp<Layer> initialParent;
- // Indicates whether the layer getting created should be added at root if there's no parent
- // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
- // be added offscreen.
- bool addToRoot;
-};
-} // namespace android
-
namespace android::surfaceflinger::frontend {
// Atomic set of changes affecting layer state. These changes are queued in binder threads and
// applied every vsync.
struct Update {
std::vector<TransactionState> transactions;
- std::vector<LayerCreatedState> layerCreatedStates;
+ std::vector<sp<Layer>> legacyLayers;
std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
std::vector<LayerCreationArgs> layerCreationArgs;
std::vector<std::pair<uint32_t, std::string /* debugName */>> destroyedHandles;
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
index 2788332..85921bb 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.cpp
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -19,8 +19,8 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <inttypes.h>
-#include <utils/Trace.h>
#include "HdrLayerInfoReporter.h"
@@ -29,7 +29,7 @@
using base::StringAppendF;
void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mHdrInfoHistory.size() == 0 || mHdrInfoHistory.back().info != info) {
mHdrInfoHistory.next() = EventHistoryEntry{info};
}
@@ -47,7 +47,7 @@
}
for (const auto& listener : toInvoke) {
- ATRACE_NAME("invoking onHdrLayerInfoChanged");
+ SFTRACE_NAME("invoking onHdrLayerInfoChanged");
listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
info.maxDesiredHdrSdrRatio);
}
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
index dfb1c1e..2088635 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -114,7 +114,7 @@
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setLayer(mSurfaceControl->get(), INT32_MAX - 2)
.setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
@@ -130,7 +130,7 @@
}
void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
- SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+ createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
}
void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
@@ -141,7 +141,7 @@
// set the ratio frame to the top right of the screen
frame.offsetBy(viewport.width - frame.width(), height >> 4);
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
.setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -167,7 +167,7 @@
}
}();
- SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+ createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
constexpr SkColor kMinRatioColor = SK_ColorBLUE;
constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
@@ -194,9 +194,21 @@
void HdrSdrRatioOverlay::animate() {
if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
.apply();
}
+SurfaceComposerClient::Transaction HdrSdrRatioOverlay::createTransaction() const {
+ constexpr float kFrameRate = 0.f;
+ constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+ const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+ SurfaceComposerClient::Transaction transaction;
+ transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+ return transaction;
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
index 72d401d..ba88252 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.h
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -53,5 +53,7 @@
size_t mIndex = 0;
std::array<sp<GraphicBuffer>, 2> mRingBuffer;
+
+ SurfaceComposerClient::Transaction createTransaction() const;
};
} // namespace android
diff --git a/services/surfaceflinger/Jank/JankTracker.cpp b/services/surfaceflinger/Jank/JankTracker.cpp
new file mode 100644
index 0000000..8e0e084
--- /dev/null
+++ b/services/surfaceflinger/Jank/JankTracker.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+#include "JankTracker.h"
+
+#include <android/gui/IJankListener.h>
+#include "BackgroundExecutor.h"
+
+namespace android {
+
+namespace {
+
+constexpr size_t kJankDataBatchSize = 50;
+
+} // anonymous namespace
+
+std::atomic<size_t> JankTracker::sListenerCount(0);
+std::atomic<bool> JankTracker::sCollectAllJankDataForTesting(false);
+
+JankTracker::~JankTracker() {}
+
+void JankTracker::addJankListener(int32_t layerId, sp<IBinder> listener) {
+ // Increment right away, so that if an onJankData call comes in before the background thread has
+ // added this listener, it will not drop the data.
+ sListenerCount++;
+
+ BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
+ {[layerId, listener = std::move(listener)]() {
+ JankTracker& tracker = getInstance();
+ const std::lock_guard<std::mutex> _l(tracker.mLock);
+ tracker.addJankListenerLocked(layerId, listener);
+ }});
+}
+
+void JankTracker::flushJankData(int32_t layerId) {
+ BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
+ {[layerId]() { getInstance().doFlushJankData(layerId); }});
+}
+
+void JankTracker::removeJankListener(int32_t layerId, sp<IBinder> listener, int64_t afterVsync) {
+ BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
+ {[layerId, listener = std::move(listener), afterVsync]() {
+ JankTracker& tracker = getInstance();
+ const std::lock_guard<std::mutex> _l(tracker.mLock);
+ tracker.markJankListenerForRemovalLocked(layerId, listener, afterVsync);
+ }});
+}
+
+void JankTracker::onJankData(int32_t layerId, gui::JankData data) {
+ if (sListenerCount == 0) {
+ return;
+ }
+
+ BackgroundExecutor::getLowPriorityInstance().sendCallbacks(
+ {[layerId, data = std::move(data)]() {
+ JankTracker& tracker = getInstance();
+
+ tracker.mLock.lock();
+ bool hasListeners = tracker.mJankListeners.count(layerId) > 0;
+ tracker.mLock.unlock();
+
+ if (!hasListeners && !sCollectAllJankDataForTesting) {
+ return;
+ }
+
+ tracker.mJankDataLock.lock();
+ tracker.mJankData.emplace(layerId, data);
+ size_t count = tracker.mJankData.count(layerId);
+ tracker.mJankDataLock.unlock();
+
+ if (count >= kJankDataBatchSize && !sCollectAllJankDataForTesting) {
+ tracker.doFlushJankData(layerId);
+ }
+ }});
+}
+
+void JankTracker::addJankListenerLocked(int32_t layerId, sp<IBinder> listener) {
+ for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ if (it->second.mListener == listener) {
+ // Undo the duplicate increment in addJankListener.
+ sListenerCount--;
+ return;
+ }
+ }
+
+ mJankListeners.emplace(layerId, std::move(listener));
+}
+
+void JankTracker::doFlushJankData(int32_t layerId) {
+ std::vector<gui::JankData> jankData;
+ int64_t maxVsync = transferAvailableJankData(layerId, jankData);
+
+ std::vector<sp<IBinder>> toSend;
+
+ mLock.lock();
+ for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) {
+ if (!jankData.empty()) {
+ toSend.emplace_back(it->second.mListener);
+ }
+
+ int64_t removeAfter = it->second.mRemoveAfter;
+ if (removeAfter != -1 && removeAfter <= maxVsync) {
+ it = mJankListeners.erase(it);
+ sListenerCount--;
+ } else {
+ it++;
+ }
+ }
+ mLock.unlock();
+
+ for (const auto& listener : toSend) {
+ binder::Status status = interface_cast<gui::IJankListener>(listener)->onJankData(jankData);
+ if (status.exceptionCode() == binder::Status::EX_NULL_POINTER) {
+ // Remove any listeners, where the App side has gone away, without
+ // deregistering.
+ dropJankListener(layerId, listener);
+ }
+ }
+}
+
+void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener,
+ int64_t afterVysnc) {
+ for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ if (it->second.mListener == listener) {
+ it->second.mRemoveAfter = std::max(static_cast<int64_t>(0), afterVysnc);
+ return;
+ }
+ }
+}
+
+int64_t JankTracker::transferAvailableJankData(int32_t layerId,
+ std::vector<gui::JankData>& outJankData) {
+ const std::lock_guard<std::mutex> _l(mJankDataLock);
+ int64_t maxVsync = 0;
+ auto range = mJankData.equal_range(layerId);
+ for (auto it = range.first; it != range.second;) {
+ maxVsync = std::max(it->second.frameVsyncId, maxVsync);
+ outJankData.emplace_back(std::move(it->second));
+ it = mJankData.erase(it);
+ }
+ return maxVsync;
+}
+
+void JankTracker::dropJankListener(int32_t layerId, sp<IBinder> listener) {
+ const std::lock_guard<std::mutex> _l(mLock);
+ for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ if (it->second.mListener == listener) {
+ mJankListeners.erase(it);
+ sListenerCount--;
+ return;
+ }
+ }
+}
+
+void JankTracker::clearAndStartCollectingAllJankDataForTesting() {
+ BackgroundExecutor::getLowPriorityInstance().flushQueue();
+
+ // Clear all past tracked jank data.
+ JankTracker& tracker = getInstance();
+ const std::lock_guard<std::mutex> _l(tracker.mJankDataLock);
+ tracker.mJankData.clear();
+
+ // Pretend there's at least one listener.
+ sListenerCount++;
+ sCollectAllJankDataForTesting = true;
+}
+
+std::vector<gui::JankData> JankTracker::getCollectedJankDataForTesting(int32_t layerId) {
+ JankTracker& tracker = getInstance();
+ const std::lock_guard<std::mutex> _l(tracker.mJankDataLock);
+
+ auto range = tracker.mJankData.equal_range(layerId);
+ std::vector<gui::JankData> result;
+ std::transform(range.first, range.second, std::back_inserter(result),
+ [](std::pair<int32_t, gui::JankData> layerIdToJankData) {
+ return layerIdToJankData.second;
+ });
+
+ return result;
+}
+
+void JankTracker::clearAndStopCollectingAllJankDataForTesting() {
+ // Undo startCollectingAllJankDataForTesting.
+ sListenerCount--;
+ sCollectAllJankDataForTesting = false;
+
+ // Clear all tracked jank data.
+ JankTracker& tracker = getInstance();
+ const std::lock_guard<std::mutex> _l(tracker.mJankDataLock);
+ tracker.mJankData.clear();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Jank/JankTracker.h b/services/surfaceflinger/Jank/JankTracker.h
new file mode 100644
index 0000000..5917358
--- /dev/null
+++ b/services/surfaceflinger/Jank/JankTracker.h
@@ -0,0 +1,99 @@
+/*
+ * 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 <cstdint>
+#include <mutex>
+#include <unordered_map>
+
+#include <android/gui/JankData.h>
+#include <binder/IBinder.h>
+#include <utils/Mutex.h>
+
+namespace android {
+namespace frametimeline {
+class FrameTimelineTest;
+}
+
+/**
+ * JankTracker maintains a backlog of frame jank classification and manages and notififies any
+ * registered jank data listeners.
+ */
+class JankTracker {
+public:
+ ~JankTracker();
+
+ static void addJankListener(int32_t layerId, sp<IBinder> listener);
+ static void flushJankData(int32_t layerId);
+ static void removeJankListener(int32_t layerId, sp<IBinder> listener, int64_t afterVysnc);
+
+ static void onJankData(int32_t layerId, gui::JankData data);
+
+protected:
+ // The following methods can be used to force the tracker to collect all jank data and not
+ // flush it for a short time period and should *only* be used for testing. Every call to
+ // clearAndStartCollectingAllJankDataForTesting needs to be followed by a call to
+ // clearAndStopCollectingAllJankDataForTesting.
+ static void clearAndStartCollectingAllJankDataForTesting();
+ static std::vector<gui::JankData> getCollectedJankDataForTesting(int32_t layerId);
+ static void clearAndStopCollectingAllJankDataForTesting();
+
+ friend class frametimeline::FrameTimelineTest;
+
+private:
+ JankTracker() {}
+ JankTracker(const JankTracker&) = delete;
+ JankTracker(JankTracker&&) = delete;
+
+ JankTracker& operator=(const JankTracker&) = delete;
+ JankTracker& operator=(JankTracker&&) = delete;
+
+ static JankTracker& getInstance() {
+ static JankTracker instance;
+ return instance;
+ }
+
+ void addJankListenerLocked(int32_t layerId, sp<IBinder> listener) REQUIRES(mLock);
+ void doFlushJankData(int32_t layerId);
+ void markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener, int64_t afterVysnc)
+ REQUIRES(mLock);
+
+ int64_t transferAvailableJankData(int32_t layerId, std::vector<gui::JankData>& jankData);
+ void dropJankListener(int32_t layerId, sp<IBinder> listener);
+
+ struct Listener {
+ sp<IBinder> mListener;
+ int64_t mRemoveAfter;
+
+ Listener(sp<IBinder>&& listener) : mListener(listener), mRemoveAfter(-1) {}
+ };
+
+ // We keep track of the current listener count, so that the onJankData call, which is on the
+ // main thread, can short-curcuit the scheduling on the background thread (which involves
+ // locking) if there are no listeners registered, which is the most common case.
+ static std::atomic<size_t> sListenerCount;
+ static std::atomic<bool> sCollectAllJankDataForTesting;
+
+ std::mutex mLock;
+ std::unordered_multimap<int32_t, Listener> mJankListeners GUARDED_BY(mLock);
+ std::mutex mJankDataLock;
+ std::unordered_multimap<int32_t, gui::JankData> mJankData GUARDED_BY(mJankDataLock);
+
+ friend class JankTrackerTest;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c39b757..dcb0812 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -27,6 +27,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <binder/IPCThreadState.h>
+#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/LayerFECompositionState.h>
@@ -39,7 +40,6 @@
#include <ftl/fake_guard.h>
#include <gui/BufferItem.h>
#include <gui/Surface.h>
-#include <gui/TraceUtils.h>
#include <math.h>
#include <private/android_filesystem_config.h>
#include <renderengine/RenderEngine.h>
@@ -58,12 +58,9 @@
#include <utils/Log.h>
#include <utils/NativeHandle.h>
#include <utils/StopWatch.h>
-#include <utils/Trace.h>
#include <algorithm>
-#include <mutex>
#include <optional>
-#include <sstream>
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
@@ -73,7 +70,6 @@
#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
-#include "MutexUtils.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TransactionCallbackInvoker.h"
@@ -90,18 +86,6 @@
const ui::Transform kIdentityTransform;
-ui::LogicalDisplayId toLogicalDisplayId(const ui::LayerStack& layerStack) {
- return ui::LogicalDisplayId{static_cast<int32_t>(layerStack.id)};
-}
-
-bool assignTransform(ui::Transform* dst, ui::Transform& from) {
- if (*dst == from) {
- return false;
- }
- *dst = from;
- return true;
-}
-
TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
@@ -150,24 +134,11 @@
: sequence(args.sequence),
mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)),
mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
- mClientRef(args.client),
mWindowType(static_cast<WindowInfo::Type>(
- args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
- mLayerCreationFlags(args.flags),
- mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName, this)) {
+ args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) {
ALOGV("Creating Layer %s", getDebugName());
- uint32_t layerFlags = 0;
- if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
- if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
- if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
- if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
- layerFlags |= layer_state_t::eLayerSkipScreenshot;
- mDrawingState.flags = layerFlags;
mDrawingState.crop.makeInvalid();
- mDrawingState.z = 0;
- mDrawingState.color.a = 1.0f;
- mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK;
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
@@ -180,33 +151,9 @@
mDrawingState.acquireFence = sp<Fence>::make(-1);
mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
- mDrawingState.hdrMetadata.validTypes = 0;
- mDrawingState.surfaceDamageRegion = Region::INVALID_REGION;
- mDrawingState.cornerRadius = 0.0f;
- mDrawingState.backgroundBlurRadius = 0;
- mDrawingState.api = -1;
- mDrawingState.hasColorTransform = false;
- mDrawingState.colorSpaceAgnostic = false;
- mDrawingState.frameRateSelectionPriority = PRIORITY_UNSET;
mDrawingState.metadata = args.metadata;
- mDrawingState.shadowRadius = 0.f;
- mDrawingState.fixedTransformHint = ui::Transform::ROT_INVALID;
mDrawingState.frameTimelineInfo = {};
mDrawingState.postTime = -1;
- mDrawingState.destinationFrame.makeInvalid();
- mDrawingState.isTrustedOverlay = false;
- mDrawingState.dropInputMode = gui::DropInputMode::NONE;
- mDrawingState.dimmingEnabled = true;
- mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
- mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Propagate;
-
- if (args.flags & ISurfaceComposerClient::eNoColorFill) {
- // Set an invalid color so there is no color fill.
- mDrawingState.color.r = -1.0_hf;
- mDrawingState.color.g = -1.0_hf;
- mDrawingState.color.b = -1.0_hf;
- }
-
mFrameTracker.setDisplayRefreshPeriod(
args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
@@ -214,14 +161,9 @@
mOwnerPid = args.ownerPid;
mOwnerAppId = mOwnerUid % PER_USER_RANGE;
- mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
- mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
-
- mSnapshot->sequence = sequence;
- mSnapshot->name = getDebugName();
- mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
- mSnapshot->parentTransform = {};
+ mLayerFEs.emplace_back(frontend::LayerHierarchy::TraversalPath{static_cast<uint32_t>(sequence)},
+ args.flinger->getFactory().createLayerFE(mName, this));
}
void Layer::onFirstRef() {
@@ -232,10 +174,6 @@
LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId,
"Layer destructor called off the main thread.");
- // The original layer and the clone layer share the same texture and buffer. Therefore, only
- // one of the layers, in this case the original layer, needs to handle the deletion. The
- // original layer and the clone should be removed at the same time so there shouldn't be any
- // issue with the clone layer trying to use the texture.
if (mBufferInfo.mBuffer != nullptr) {
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
@@ -251,10 +189,6 @@
if (mDrawingState.sidebandStream != nullptr) {
mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
}
- if (mHadClonedChild) {
- auto& roots = mFlinger->mLayerMirrorRoots;
- roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end());
- }
if (hasTrustedPresentationListener()) {
mFlinger->mNumTrustedPresentationListeners--;
updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/);
@@ -262,78 +196,8 @@
}
// ---------------------------------------------------------------------------
-// callbacks
-// ---------------------------------------------------------------------------
-
-void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
- if (mDrawingState.zOrderRelativeOf == nullptr) {
- return;
- }
-
- sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
- if (strongRelative == nullptr) {
- setZOrderRelativeOf(nullptr);
- return;
- }
-
- if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) {
- strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this));
- mFlinger->setTransactionFlags(eTraversalNeeded);
- setZOrderRelativeOf(nullptr);
- }
-}
-
-void Layer::removeFromCurrentState() {
- if (!mRemovedFromDrawingState) {
- mRemovedFromDrawingState = true;
- mFlinger->mScheduler->deregisterLayer(this);
- }
- updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/);
-
- mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this));
-}
-
-sp<Layer> Layer::getRootLayer() {
- sp<Layer> parent = getParent();
- if (parent == nullptr) {
- return sp<Layer>::fromExisting(this);
- }
- return parent->getRootLayer();
-}
-
-void Layer::onRemovedFromCurrentState() {
- // Use the root layer since we want to maintain the hierarchy for the entire subtree.
- auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current);
- std::sort(layersInTree.begin(), layersInTree.end());
-
- REQUIRE_MUTEX(mFlinger->mStateLock);
- traverse(LayerVector::StateSet::Current,
- [&](Layer* layer) REQUIRES(layer->mFlinger->mStateLock) {
- layer->removeFromCurrentState();
- layer->removeRelativeZ(layersInTree);
- });
-}
-
-void Layer::addToCurrentState() {
- if (mRemovedFromDrawingState) {
- mRemovedFromDrawingState = false;
- mFlinger->mScheduler->registerLayer(this);
- mFlinger->removeFromOffscreenLayers(this);
- }
-
- for (const auto& child : mCurrentChildren) {
- child->addToCurrentState();
- }
-}
-
-// ---------------------------------------------------------------------------
// set-up
// ---------------------------------------------------------------------------
-
-bool Layer::getPremultipledAlpha() const {
- return mPremultipliedAlpha;
-}
-
sp<IBinder> Layer::getHandle() {
Mutex::Autolock _l(mLock);
if (mGetHandleCalled) {
@@ -349,46 +213,6 @@
// h/w composer set-up
// ---------------------------------------------------------------------------
-static Rect reduce(const Rect& win, const Region& exclude) {
- if (CC_LIKELY(exclude.isEmpty())) {
- return win;
- }
- if (exclude.isRect()) {
- return win.reduce(exclude.getBounds());
- }
- return Region(win).subtract(exclude).getBounds();
-}
-
-static FloatRect reduce(const FloatRect& win, const Region& exclude) {
- if (CC_LIKELY(exclude.isEmpty())) {
- return win;
- }
- // Convert through Rect (by rounding) for lack of FloatRegion
- return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
-}
-
-Rect Layer::getScreenBounds(bool reduceTransparentRegion) const {
- if (!reduceTransparentRegion) {
- return Rect{mScreenBounds};
- }
-
- FloatRect bounds = getBounds();
- ui::Transform t = getTransform();
- // Transform to screen space.
- bounds = t.transform(bounds);
- return Rect{bounds};
-}
-
-FloatRect Layer::getBounds() const {
- const State& s(getDrawingState());
- return getBounds(getActiveTransparentRegion(s));
-}
-
-FloatRect Layer::getBounds(const Region& activeTransparentRegion) const {
- // Subtract the transparent region and snap to the bounds.
- return reduce(mBounds, activeTransparentRegion);
-}
-
// No early returns.
void Layer::updateTrustedPresentationState(const DisplayDevice* display,
const frontend::LayerSnapshot* snapshot,
@@ -490,57 +314,6 @@
return true;
}
-void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
- float parentShadowRadius) {
- const State& s(getDrawingState());
-
- // Calculate effective layer transform
- mEffectiveTransform = parentTransform * getActiveTransform(s);
-
- if (CC_UNLIKELY(!isTransformValid())) {
- ALOGW("Stop computing bounds for %s because it has invalid transformation.",
- getDebugName());
- return;
- }
-
- // Transform parent bounds to layer space
- parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
-
- // Calculate source bounds
- mSourceBounds = computeSourceBounds(parentBounds);
-
- // Calculate bounds by croping diplay frame with layer crop and parent bounds
- FloatRect bounds = mSourceBounds;
- const Rect layerCrop = getCrop(s);
- if (!layerCrop.isEmpty()) {
- bounds = mSourceBounds.intersect(layerCrop.toFloatRect());
- }
- bounds = bounds.intersect(parentBounds);
-
- mBounds = bounds;
- mScreenBounds = mEffectiveTransform.transform(mBounds);
-
- // Use the layer's own shadow radius if set. Otherwise get the radius from
- // parent.
- if (s.shadowRadius > 0.f) {
- mEffectiveShadowRadius = s.shadowRadius;
- } else {
- mEffectiveShadowRadius = parentShadowRadius;
- }
-
- // Shadow radius is passed down to only one layer so if the layer can draw shadows,
- // don't pass it to its children.
- const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
-
- for (const sp<Layer>& child : mDrawingChildren) {
- child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
- }
-
- if (mPotentialCursor) {
- prepareCursorCompositionState();
- }
-}
-
Rect Layer::getCroppedBufferSize(const State& s) const {
Rect size = getBufferSize(s);
Rect crop = getCrop(s);
@@ -552,181 +325,6 @@
return size;
}
-void Layer::setupRoundedCornersCropCoordinates(Rect win,
- const FloatRect& roundedCornersCrop) const {
- // Translate win by the rounded corners rect coordinates, to have all values in
- // layer coordinate space.
- win.left -= roundedCornersCrop.left;
- win.right -= roundedCornersCrop.left;
- win.top -= roundedCornersCrop.top;
- win.bottom -= roundedCornersCrop.top;
-}
-
-void Layer::prepareBasicGeometryCompositionState() {
- const auto& drawingState{getDrawingState()};
- const auto alpha = static_cast<float>(getAlpha());
- const bool opaque = isOpaque(drawingState);
- const bool usesRoundedCorners = hasRoundedCorners();
-
- auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
- if (!opaque || alpha != 1.0f) {
- blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
- : Hwc2::IComposerClient::BlendMode::COVERAGE;
- }
-
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
- snapshot->outputFilter = getOutputFilter();
- snapshot->isVisible = isVisible();
- snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
- snapshot->shadowSettings.length = mEffectiveShadowRadius;
-
- snapshot->contentDirty = contentDirty;
- contentDirty = false;
-
- snapshot->geomLayerBounds = mBounds;
- snapshot->geomLayerTransform = getTransform();
- snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
- snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
- snapshot->localTransform = getActiveTransform(drawingState);
- snapshot->localTransformInverse = snapshot->localTransform.inverse();
- snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
- snapshot->alpha = alpha;
- snapshot->backgroundBlurRadius = getBackgroundBlurRadius();
- snapshot->blurRegions = getBlurRegions();
- snapshot->stretchEffect = getStretchEffect();
-}
-
-void Layer::prepareGeometryCompositionState() {
- const auto& drawingState{getDrawingState()};
- auto* snapshot = editLayerSnapshot();
-
- // Please keep in sync with LayerSnapshotBuilder
- snapshot->geomBufferSize = getBufferSize(drawingState);
- snapshot->geomContentCrop = getBufferCrop();
- snapshot->geomCrop = getCrop(drawingState);
- snapshot->geomBufferTransform = getBufferTransform();
- snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
- snapshot->geomUsesSourceCrop = usesSourceCrop();
- snapshot->isSecure = isSecure();
-
- snapshot->metadata.clear();
- const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
- for (const auto& [key, mandatory] : supportedMetadata) {
- const auto& genericLayerMetadataCompatibilityMap =
- mFlinger->getGenericLayerMetadataKeyMap();
- auto compatIter = genericLayerMetadataCompatibilityMap.find(key);
- if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) {
- continue;
- }
- const uint32_t id = compatIter->second;
-
- auto it = drawingState.metadata.mMap.find(id);
- if (it == std::end(drawingState.metadata.mMap)) {
- continue;
- }
-
- snapshot->metadata.emplace(key,
- compositionengine::GenericLayerMetadataEntry{mandatory,
- it->second});
- }
-}
-
-void Layer::preparePerFrameCompositionState() {
- const auto& drawingState{getDrawingState()};
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
-
- snapshot->forceClientComposition = false;
-
- snapshot->isColorspaceAgnostic = isColorSpaceAgnostic();
- snapshot->dataspace = getDataSpace();
- snapshot->colorTransform = getColorTransform();
- snapshot->colorTransformIsIdentity = !hasColorTransform();
- snapshot->surfaceDamage = surfaceDamageRegion;
- snapshot->hasProtectedContent = isProtected();
- snapshot->dimmingEnabled = isDimmingEnabled();
- snapshot->currentHdrSdrRatio = getCurrentHdrSdrRatio();
- snapshot->desiredHdrSdrRatio = getDesiredHdrSdrRatio();
- snapshot->cachingHint = getCachingHint();
-
- const bool usesRoundedCorners = hasRoundedCorners();
-
- snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
-
- // Force client composition for special cases known only to the front-end.
- // Rounded corners no longer force client composition, since we may use a
- // hole punch so that the layer will appear to have rounded corners.
- if (drawShadows() || snapshot->stretchEffect.hasEffect()) {
- snapshot->forceClientComposition = true;
- }
- // If there are no visible region changes, we still need to update blur parameters.
- snapshot->blurRegions = getBlurRegions();
- snapshot->backgroundBlurRadius = getBackgroundBlurRadius();
-
- // Layer framerate is used in caching decisions.
- // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
- // LayerFECompositionState where it would be visible to Flattener.
- snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
-
- if (hasBufferOrSidebandStream()) {
- preparePerFrameBufferCompositionState();
- } else {
- preparePerFrameEffectsCompositionState();
- }
-}
-
-void Layer::preparePerFrameBufferCompositionState() {
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
- // Sideband layers
- if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
- return;
- } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
- } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) {
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR;
- } else {
- // Normal buffer layers
- snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
- snapshot->compositionType = mPotentialCursor
- ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
- : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
- }
-
- snapshot->buffer = getBuffer();
- snapshot->acquireFence = mBufferInfo.mFence;
- snapshot->frameNumber = mBufferInfo.mFrameNumber;
- snapshot->sidebandStreamHasFrame = false;
-}
-
-void Layer::preparePerFrameEffectsCompositionState() {
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
- snapshot->color = getColor();
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
-}
-
-void Layer::prepareCursorCompositionState() {
- const State& drawingState{getDrawingState()};
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
-
- // Apply the layer's transform, followed by the display's global transform
- // Here we're guaranteed that the layer's transform preserves rects
- Rect win = getCroppedBufferSize(drawingState);
- // Subtract the transparent region and snap to the bounds
- Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
- Rect frame(getTransform().transform(bounds));
-
- snapshot->cursorFrame = frame;
-}
-
const char* Layer::getDebugName() const {
return mName.c_str();
}
@@ -754,91 +352,9 @@
}
// ----------------------------------------------------------------------------
-// local state
-// ----------------------------------------------------------------------------
-
-bool Layer::isSecure() const {
- const State& s(mDrawingState);
- if (s.flags & layer_state_t::eLayerSecure) {
- return true;
- }
-
- const auto p = mDrawingParent.promote();
- return (p != nullptr) ? p->isSecure() : false;
-}
-
-void Layer::transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
- std::vector<JankData>& jankData) {
- if (mPendingJankClassifications.empty() ||
- !mPendingJankClassifications.front()->getJankType()) {
- return;
- }
-
- bool includeJankData = false;
- for (const auto& handle : handles) {
- for (const auto& cb : handle->callbackIds) {
- if (cb.includeJankData) {
- includeJankData = true;
- break;
- }
- }
-
- if (includeJankData) {
- jankData.reserve(mPendingJankClassifications.size());
- break;
- }
- }
-
- while (!mPendingJankClassifications.empty() &&
- mPendingJankClassifications.front()->getJankType()) {
- if (includeJankData) {
- std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
- mPendingJankClassifications.front();
- jankData.emplace_back(JankData(surfaceFrame->getToken(),
- surfaceFrame->getJankType().value(),
- surfaceFrame->getRenderRate().getPeriodNsecs()));
- }
- mPendingJankClassifications.pop_front();
- }
-}
-
-// ----------------------------------------------------------------------------
// transaction
// ----------------------------------------------------------------------------
-uint32_t Layer::doTransaction(uint32_t flags) {
- ATRACE_CALL();
-
- // TODO: This is unfortunate.
- mDrawingStateModified = mDrawingState.modified;
- mDrawingState.modified = false;
-
- const State& s(getDrawingState());
-
- if (updateGeometry()) {
- // invalidate and recompute the visible regions if needed
- flags |= Layer::eVisibleRegion;
- }
-
- if (s.sequence != mLastCommittedTxSequence) {
- // invalidate and recompute the visible regions if needed
- mLastCommittedTxSequence = s.sequence;
- flags |= eVisibleRegion;
- this->contentDirty = true;
-
- // we may use linear filtering, if the matrix scales us
- mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
- }
-
- if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) {
- mFlinger->mUpdateInputInfo = true;
- }
-
- commitTransaction();
-
- return flags;
-}
-
void Layer::commitTransaction() {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
@@ -853,504 +369,25 @@
mDrawingState.bufferlessSurfaceFramesTX.clear();
}
-uint32_t Layer::clearTransactionFlags(uint32_t mask) {
- const auto flags = mTransactionFlags & mask;
- mTransactionFlags &= ~mask;
- return flags;
-}
-
void Layer::setTransactionFlags(uint32_t mask) {
mTransactionFlags |= mask;
}
-bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) {
- ssize_t idx = mCurrentChildren.indexOf(childLayer);
- if (idx < 0) {
- return false;
- }
- if (childLayer->setLayer(z)) {
- mCurrentChildren.removeAt(idx);
- mCurrentChildren.add(childLayer);
- return true;
- }
- return false;
-}
-
-bool Layer::setChildRelativeLayer(const sp<Layer>& childLayer,
- const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
- ssize_t idx = mCurrentChildren.indexOf(childLayer);
- if (idx < 0) {
- return false;
- }
- if (childLayer->setRelativeLayer(relativeToHandle, relativeZ)) {
- mCurrentChildren.removeAt(idx);
- mCurrentChildren.add(childLayer);
- return true;
- }
- return false;
-}
-
-bool Layer::setLayer(int32_t z) {
- if (mDrawingState.z == z && !usingRelativeZ(LayerVector::StateSet::Current)) return false;
- mDrawingState.sequence++;
- mDrawingState.z = z;
- mDrawingState.modified = true;
-
- mFlinger->mSomeChildrenChanged = true;
-
- // Discard all relative layering.
- if (mDrawingState.zOrderRelativeOf != nullptr) {
- sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
- if (strongRelative != nullptr) {
- strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this));
- }
- setZOrderRelativeOf(nullptr);
- }
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-void Layer::removeZOrderRelative(const wp<Layer>& relative) {
- mDrawingState.zOrderRelatives.remove(relative);
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-}
-
-void Layer::addZOrderRelative(const wp<Layer>& relative) {
- mDrawingState.zOrderRelatives.add(relative);
- mDrawingState.modified = true;
- mDrawingState.sequence++;
- setTransactionFlags(eTransactionNeeded);
-}
-
-void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) {
- mDrawingState.zOrderRelativeOf = relativeOf;
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- mDrawingState.isRelativeOf = relativeOf != nullptr;
-
- setTransactionFlags(eTransactionNeeded);
-}
-
-bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
- sp<Layer> relative = LayerHandle::getLayer(relativeToHandle);
- if (relative == nullptr) {
- return false;
- }
-
- if (mDrawingState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
- mDrawingState.zOrderRelativeOf == relative) {
- return false;
- }
-
- if (CC_UNLIKELY(relative->usingRelativeZ(LayerVector::StateSet::Drawing)) &&
- (relative->mDrawingState.zOrderRelativeOf == this)) {
- ALOGE("Detected relative layer loop between %s and %s",
- mName.c_str(), relative->mName.c_str());
- ALOGE("Ignoring new call to set relative layer");
- return false;
- }
-
- mFlinger->mSomeChildrenChanged = true;
-
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- mDrawingState.z = relativeZ;
-
- auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
- if (oldZOrderRelativeOf != nullptr) {
- oldZOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this));
- }
- setZOrderRelativeOf(relative);
- relative->addZOrderRelative(wp<Layer>::fromExisting(this));
-
- setTransactionFlags(eTransactionNeeded);
-
- return true;
-}
-
-bool Layer::setTrustedOverlay(bool isTrustedOverlay) {
- if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false;
- mDrawingState.isTrustedOverlay = isTrustedOverlay;
- mDrawingState.modified = true;
- mFlinger->mUpdateInputInfo = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::isTrustedOverlay() const {
- if (getDrawingState().isTrustedOverlay) {
- return true;
- }
- const auto& p = mDrawingParent.promote();
- return (p != nullptr) && p->isTrustedOverlay();
-}
-
-bool Layer::setAlpha(float alpha) {
- if (mDrawingState.color.a == alpha) return false;
- mDrawingState.sequence++;
- mDrawingState.color.a = alpha;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) {
- if (!mDrawingState.bgColorLayer && alpha == 0) {
- return false;
- }
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- if (!mDrawingState.bgColorLayer && alpha != 0) {
- // create background color layer if one does not yet exist
- uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect;
- std::string name = mName + "BackgroundColorLayer";
- mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer(
- surfaceflinger::LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags,
- LayerMetadata()));
-
- // add to child list
- addChild(mDrawingState.bgColorLayer);
- mFlinger->mLayersAdded = true;
- // set up SF to handle added color layer
- if (isRemovedFromCurrentState()) {
- MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock);
- mDrawingState.bgColorLayer->onRemovedFromCurrentState();
- }
- mFlinger->setTransactionFlags(eTransactionNeeded);
- } else if (mDrawingState.bgColorLayer && alpha == 0) {
- MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock);
- mDrawingState.bgColorLayer->reparent(nullptr);
- mDrawingState.bgColorLayer = nullptr;
- return true;
- }
-
- mDrawingState.bgColorLayer->setColor(color);
- mDrawingState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min());
- mDrawingState.bgColorLayer->setAlpha(alpha);
- mDrawingState.bgColorLayer->setDataspace(dataspace);
-
- return true;
-}
-
-bool Layer::setCornerRadius(float cornerRadius) {
- if (mDrawingState.cornerRadius == cornerRadius) return false;
-
- mDrawingState.sequence++;
- mDrawingState.cornerRadius = cornerRadius;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {
- if (mDrawingState.backgroundBlurRadius == backgroundBlurRadius) return false;
- // If we start or stop drawing blur then the layer's visibility state may change so increment
- // the magic sequence number.
- if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) {
- mDrawingState.sequence++;
- }
- mDrawingState.backgroundBlurRadius = backgroundBlurRadius;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setTransparentRegionHint(const Region& transparent) {
- mDrawingState.sequence++;
- mDrawingState.transparentRegionHint = transparent;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
- // If we start or stop drawing blur then the layer's visibility state may change so increment
- // the magic sequence number.
- if (mDrawingState.blurRegions.size() == 0 || blurRegions.size() == 0) {
- mDrawingState.sequence++;
- }
- mDrawingState.blurRegions = blurRegions;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFlags(uint32_t flags, uint32_t mask) {
- const uint32_t newFlags = (mDrawingState.flags & ~mask) | (flags & mask);
- if (mDrawingState.flags == newFlags) return false;
- mDrawingState.sequence++;
- mDrawingState.flags = newFlags;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
bool Layer::setCrop(const Rect& crop) {
if (mDrawingState.crop == crop) return false;
mDrawingState.sequence++;
mDrawingState.crop = crop;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool Layer::setMetadata(const LayerMetadata& data) {
- if (!mDrawingState.metadata.merge(data, true /* eraseEmpty */)) return false;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setLayerStack(ui::LayerStack layerStack) {
- if (mDrawingState.layerStack == layerStack) return false;
- mDrawingState.sequence++;
- mDrawingState.layerStack = layerStack;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setColorSpaceAgnostic(const bool agnostic) {
- if (mDrawingState.colorSpaceAgnostic == agnostic) {
- return false;
- }
- mDrawingState.sequence++;
- mDrawingState.colorSpaceAgnostic = agnostic;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setDimmingEnabled(const bool dimmingEnabled) {
- if (mDrawingState.dimmingEnabled == dimmingEnabled) return false;
-
- mDrawingState.sequence++;
- mDrawingState.dimmingEnabled = dimmingEnabled;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFrameRateSelectionPriority(int32_t priority) {
- if (mDrawingState.frameRateSelectionPriority == priority) return false;
- mDrawingState.frameRateSelectionPriority = priority;
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-int32_t Layer::getFrameRateSelectionPriority() const {
- // Check if layer has priority set.
- if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
- return mDrawingState.frameRateSelectionPriority;
- }
- // If not, search whether its parents have it set.
- sp<Layer> parent = getParent();
- if (parent != nullptr) {
- return parent->getFrameRateSelectionPriority();
- }
-
- return Layer::PRIORITY_UNSET;
-}
-
-bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) {
- if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
- mDrawingState.defaultFrameRateCompatibility = compatibility;
- mDrawingState.modified = true;
- mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility);
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-scheduler::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
- return mDrawingState.defaultFrameRateCompatibility;
-}
-
bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
-ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const {
- bool useDrawing = state == LayerVector::StateSet::Drawing;
- const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent) {
- return parent->getLayerStack();
- }
- return getDrawingState().layerStack;
-}
-
-bool Layer::setShadowRadius(float shadowRadius) {
- if (mDrawingState.shadowRadius == shadowRadius) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.shadowRadius = shadowRadius;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint) {
- if (mDrawingState.fixedTransformHint == fixedTransformHint) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.fixedTransformHint = fixedTransformHint;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setStretchEffect(const StretchEffect& effect) {
- StretchEffect temp = effect;
- temp.sanitize();
- if (mDrawingState.stretchEffect == temp) {
- return false;
- }
- mDrawingState.sequence++;
- mDrawingState.stretchEffect = temp;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-StretchEffect Layer::getStretchEffect() const {
- if (mDrawingState.stretchEffect.hasEffect()) {
- return mDrawingState.stretchEffect;
- }
-
- sp<Layer> parent = getParent();
- if (parent != nullptr) {
- auto effect = parent->getStretchEffect();
- if (effect.hasEffect()) {
- // TODO(b/179047472): Map it? Or do we make the effect be in global space?
- return effect;
- }
- }
- return StretchEffect{};
-}
-
-bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
- bool* transactionNeeded) {
- // Gets the frame rate to propagate to children.
- const auto frameRate = [&] {
- if (overrideChildren && parentFrameRate.isValid()) {
- return parentFrameRate;
- }
-
- if (mDrawingState.frameRate.isValid()) {
- return mDrawingState.frameRate;
- }
-
- return parentFrameRate;
- }();
-
- auto now = systemTime();
- *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
-
- // The frame rate is propagated to the children by default, but some properties may override it.
- bool childrenHaveFrameRate = false;
- const bool overrideChildrenFrameRate = overrideChildren || shouldOverrideChildrenFrameRate();
- const bool canPropagateFrameRate = shouldPropagateFrameRate() || overrideChildrenFrameRate;
- for (const sp<Layer>& child : mCurrentChildren) {
- childrenHaveFrameRate |=
- child->propagateFrameRateForLayerTree(canPropagateFrameRate ? frameRate
- : FrameRate(),
- overrideChildrenFrameRate, transactionNeeded);
- }
-
- // If we don't have a valid frame rate specification, but the children do, we set this
- // layer as NoVote to allow the children to control the refresh rate
- if (!frameRate.isValid() && childrenHaveFrameRate) {
- *transactionNeeded |=
- setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote),
- now);
- }
-
- // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for
- // the same reason we are allowing touch boost for those layers. See
- // RefreshRateSelector::rankFrameRates for details.
- const auto layerVotedWithDefaultCompatibility =
- frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Default;
- const auto layerVotedWithNoVote = frameRate.vote.type == FrameRateCompatibility::NoVote;
- const auto layerVotedWithCategory = frameRate.category != FrameRateCategory::Default;
- const auto layerVotedWithExactCompatibility =
- frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Exact;
- return layerVotedWithDefaultCompatibility || layerVotedWithNoVote || layerVotedWithCategory ||
- layerVotedWithExactCompatibility || childrenHaveFrameRate;
-}
-
-void Layer::updateTreeHasFrameRateVote() {
- const auto root = [&]() -> sp<Layer> {
- sp<Layer> layer = sp<Layer>::fromExisting(this);
- while (auto parent = layer->getParent()) {
- layer = parent;
- }
- return layer;
- }();
-
- bool transactionNeeded = false;
- root->propagateFrameRateForLayerTree({}, false, &transactionNeeded);
-
- // TODO(b/195668952): we probably don't need eTraversalNeeded here
- if (transactionNeeded) {
- mFlinger->setTransactionFlags(eTraversalNeeded);
- }
-}
-
-bool Layer::setFrameRate(FrameRate::FrameRateVote frameRateVote) {
- if (mDrawingState.frameRate.vote == frameRateVote) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.frameRate.vote = frameRateVote;
- mDrawingState.modified = true;
-
- updateTreeHasFrameRateVote();
-
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFrameRateCategory(FrameRateCategory category, bool smoothSwitchOnly) {
- if (mDrawingState.frameRate.category == category &&
- mDrawingState.frameRate.categorySmoothSwitchOnly == smoothSwitchOnly) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.frameRate.category = category;
- mDrawingState.frameRate.categorySmoothSwitchOnly = smoothSwitchOnly;
- mDrawingState.modified = true;
-
- updateTreeHasFrameRateVote();
-
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFrameRateSelectionStrategy(FrameRateSelectionStrategy strategy) {
- if (mDrawingState.frameRateSelectionStrategy == strategy) return false;
- mDrawingState.frameRateSelectionStrategy = strategy;
- mDrawingState.sequence++;
- mDrawingState.modified = true;
-
- updateTreeHasFrameRateVote();
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime) {
+ nsecs_t postTime, gui::GameMode gameMode) {
mDrawingState.postTime = postTime;
// Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if
@@ -1366,17 +403,17 @@
mDrawingState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
} else {
mDrawingState.bufferSurfaceFrameTX =
- createSurfaceFrameForBuffer(info, postTime, mTransactionName);
+ createSurfaceFrameForBuffer(info, postTime, mTransactionName, gameMode);
}
- setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode);
}
void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime) {
+ nsecs_t postTime,
+ gui::GameMode gameMode) {
mDrawingState.frameTimelineInfo = info;
mDrawingState.postTime = postTime;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
if (const auto& bufferSurfaceFrameTX = mDrawingState.bufferSurfaceFrameTX;
@@ -1392,17 +429,17 @@
// targeting different vsyncs).
auto it = mDrawingState.bufferlessSurfaceFramesTX.find(info.vsyncId);
if (it == mDrawingState.bufferlessSurfaceFramesTX.end()) {
- auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime);
+ auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime, gameMode);
mDrawingState.bufferlessSurfaceFramesTX[info.vsyncId] = surfaceFrame;
} else {
if (it->second->getPresentState() == PresentState::Presented) {
// If the SurfaceFrame was already presented, its safe to overwrite it since it must
// have been from previous vsync.
- it->second = createSurfaceFrameForTransaction(info, postTime);
+ it->second = createSurfaceFrameForTransaction(info, postTime, gameMode);
}
}
- setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode);
}
void Layer::addSurfaceFrameDroppedForBuffer(
@@ -1422,12 +459,12 @@
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
- const FrameTimelineInfo& info, nsecs_t postTime) {
+ const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
mTransactionName,
- /*isBuffer*/ false, getGameMode());
+ /*isBuffer*/ false, gameMode);
surfaceFrame->setActualStartTime(info.startTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1436,16 +473,16 @@
if (fps) {
surfaceFrame->setRenderRate(*fps);
}
- onSurfaceFrameCreated(surfaceFrame);
return surfaceFrame;
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
- const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
+ const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
+ gui::GameMode gameMode) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
- /*isBuffer*/ true, getGameMode());
+ /*isBuffer*/ true, gameMode);
surfaceFrame->setActualStartTime(info.startTimeNanos);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
@@ -1453,12 +490,11 @@
if (fps) {
surfaceFrame->setRenderRate(*fps);
}
- onSurfaceFrameCreated(surfaceFrame);
return surfaceFrame;
}
void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
- std::string debugName) {
+ std::string debugName, gui::GameMode gameMode) {
if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return;
}
@@ -1470,7 +506,7 @@
mFlinger->mFrameTimeline->createSurfaceFrameForToken(skippedFrameTimelineInfo,
mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
- /*isBuffer*/ false, getGameMode());
+ /*isBuffer*/ false, gameMode);
surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1479,29 +515,9 @@
if (fps) {
surfaceFrame->setRenderRate(*fps);
}
- onSurfaceFrameCreated(surfaceFrame);
addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
}
-bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) {
- if (mDrawingState.frameRateForLayerTree == frameRate) {
- return false;
- }
-
- mDrawingState.frameRateForLayerTree = frameRate;
-
- // TODO(b/195668952): we probably don't need to dirty visible regions here
- // or even store frameRateForLayerTree in mDrawingState
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- mFlinger->mScheduler
- ->recordLayerHistory(sequence, getLayerProps(), now, now,
- scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
- return true;
-}
-
bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps,
nsecs_t now) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
@@ -1519,48 +535,6 @@
return getDrawingState().frameRateForLayerTree;
}
-bool Layer::isHiddenByPolicy() const {
- const State& s(mDrawingState);
- const auto& parent = mDrawingParent.promote();
- if (parent != nullptr && parent->isHiddenByPolicy()) {
- return true;
- }
- if (usingRelativeZ(LayerVector::StateSet::Drawing)) {
- auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- if (zOrderRelativeOf->isHiddenByPolicy()) {
- return true;
- }
- }
- }
- if (CC_UNLIKELY(!isTransformValid())) {
- ALOGW("Hide layer %s because it has invalid transformation.", getDebugName());
- return true;
- }
- return s.flags & layer_state_t::eLayerHidden;
-}
-
-uint32_t Layer::getEffectiveUsage(uint32_t usage) const {
- // TODO: should we do something special if mSecure is set?
- if (mProtectedByApp) {
- // need a hardware-protected path to external video sink
- usage |= GraphicBuffer::USAGE_PROTECTED;
- }
- if (mPotentialCursor) {
- usage |= GraphicBuffer::USAGE_CURSOR;
- }
- usage |= GraphicBuffer::USAGE_HW_COMPOSER;
- return usage;
-}
-
-void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
- if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
- transformHint = ui::Transform::ROT_0;
- }
-
- setTransformHintLegacy(transformHint);
-}
-
// ----------------------------------------------------------------------------
// debugging
// ----------------------------------------------------------------------------
@@ -1580,57 +554,6 @@
result.append("\n");
}
-void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const {
- const auto outputLayer = findOutputLayerForDisplay(&display);
- if (!outputLayer) {
- return;
- }
-
- std::string name;
- if (mName.length() > 77) {
- std::string shortened;
- shortened.append(mName, 0, 36);
- shortened.append("[...]");
- shortened.append(mName, mName.length() - 36);
- name = std::move(shortened);
- } else {
- name = mName;
- }
-
- StringAppendF(&result, " %s\n", name.c_str());
-
- const State& layerState(getDrawingState());
- const auto& outputLayerState = outputLayer->getState();
-
- if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
- StringAppendF(&result, " rel %6d | ", layerState.z);
- } else {
- StringAppendF(&result, " %10d | ", layerState.z);
- }
- StringAppendF(&result, " %10d | ", mWindowType);
- StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str());
- StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
- const Rect& frame = outputLayerState.displayFrame;
- StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
- const FloatRect& crop = outputLayerState.sourceCrop;
- StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
- crop.bottom);
- const auto frameRate = getFrameRateForLayerTree();
- if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(),
- ftl::enum_string(frameRate.vote.type).c_str(),
- ftl::enum_string(frameRate.vote.seamlessness).c_str());
- } else {
- result.append(41, ' ');
- }
-
- const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
- StringAppendF(&result, " [%s]\n", focused ? "*" : " ");
-
- result.append(kDumpTableRowLength, '-');
- result.append("\n");
-}
-
void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot,
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path);
@@ -1690,507 +613,12 @@
mFrameTracker.getStats(outStats);
}
-void Layer::dumpOffscreenDebugInfo(std::string& result) const {
- std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
- StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(),
- mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : "");
-}
-
void Layer::onDisconnect() {
const int32_t layerId = getSequence();
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
}
-size_t Layer::getDescendantCount() const {
- size_t count = 0;
- for (const sp<Layer>& child : mDrawingChildren) {
- count += 1 + child->getChildrenCount();
- }
- return count;
-}
-
-void Layer::setGameModeForTree(GameMode gameMode) {
- const auto& currentState = getDrawingState();
- if (currentState.metadata.has(gui::METADATA_GAME_MODE)) {
- gameMode =
- static_cast<GameMode>(currentState.metadata.getInt32(gui::METADATA_GAME_MODE, 0));
- }
- setGameMode(gameMode);
- for (const sp<Layer>& child : mCurrentChildren) {
- child->setGameModeForTree(gameMode);
- }
-}
-
-void Layer::addChild(const sp<Layer>& layer) {
- mFlinger->mSomeChildrenChanged = true;
- setTransactionFlags(eTransactionNeeded);
-
- mCurrentChildren.add(layer);
- layer->setParent(sp<Layer>::fromExisting(this));
- layer->setGameModeForTree(mGameMode);
- updateTreeHasFrameRateVote();
-}
-
-ssize_t Layer::removeChild(const sp<Layer>& layer) {
- mFlinger->mSomeChildrenChanged = true;
- setTransactionFlags(eTransactionNeeded);
-
- layer->setParent(nullptr);
- const auto removeResult = mCurrentChildren.remove(layer);
-
- updateTreeHasFrameRateVote();
- layer->setGameModeForTree(GameMode::Unsupported);
- layer->updateTreeHasFrameRateVote();
-
- return removeResult;
-}
-
-void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
- for (const sp<Layer>& child : mDrawingChildren) {
- child->mDrawingParent = newParent;
- const float parentShadowRadius =
- newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius;
- child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
- parentShadowRadius);
- }
-}
-
-bool Layer::reparent(const sp<IBinder>& newParentHandle) {
- sp<Layer> newParent;
- if (newParentHandle != nullptr) {
- newParent = LayerHandle::getLayer(newParentHandle);
- if (newParent == nullptr) {
- ALOGE("Unable to promote Layer handle");
- return false;
- }
- if (newParent == this) {
- ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str());
- return false;
- }
- }
-
- sp<Layer> parent = getParent();
- if (parent != nullptr) {
- parent->removeChild(sp<Layer>::fromExisting(this));
- }
-
- if (newParentHandle != nullptr) {
- newParent->addChild(sp<Layer>::fromExisting(this));
- if (!newParent->isRemovedFromCurrentState()) {
- addToCurrentState();
- } else {
- onRemovedFromCurrentState();
- }
- } else {
- onRemovedFromCurrentState();
- }
-
- return true;
-}
-
-bool Layer::setColorTransform(const mat4& matrix) {
- static const mat4 identityMatrix = mat4();
-
- if (mDrawingState.colorTransform == matrix) {
- return false;
- }
- ++mDrawingState.sequence;
- mDrawingState.colorTransform = matrix;
- mDrawingState.hasColorTransform = matrix != identityMatrix;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-mat4 Layer::getColorTransform() const {
- mat4 colorTransform = mat4(getDrawingState().colorTransform);
- if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
- colorTransform = parent->getColorTransform() * colorTransform;
- }
- return colorTransform;
-}
-
-bool Layer::hasColorTransform() const {
- bool hasColorTransform = getDrawingState().hasColorTransform;
- if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
- hasColorTransform = hasColorTransform || parent->hasColorTransform();
- }
- return hasColorTransform;
-}
-
-bool Layer::isLegacyDataSpace() const {
- // return true when no higher bits are set
- return !(getDataSpace() &
- (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK |
- ui::Dataspace::RANGE_MASK));
-}
-
-void Layer::setParent(const sp<Layer>& layer) {
- mCurrentParent = layer;
-}
-
-int32_t Layer::getZ(LayerVector::StateSet) const {
- return mDrawingState.z;
-}
-
-bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
- return state.isRelativeOf;
-}
-
-__attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList(
- LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers) {
- LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
- "makeTraversalList received invalid stateSet");
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
-
- if (state.zOrderRelatives.size() == 0) {
- *outSkipRelativeZUsers = true;
- return children;
- }
-
- LayerVector traverse(stateSet);
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- traverse.add(strongRelative);
- }
- }
-
- for (const sp<Layer>& child : children) {
- if (child->usingRelativeZ(stateSet)) {
- continue;
- }
- traverse.add(child);
- }
-
- return traverse;
-}
-
-/**
- * Negatively signed relatives are before 'this' in Z-order.
- */
-void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor) {
- // In the case we have other layers who are using a relative Z to us, makeTraversalList will
- // produce a new list for traversing, including our relatives, and not including our children
- // who are relatives of another surface. In the case that there are no relative Z,
- // makeTraversalList returns our children directly to avoid significant overhead.
- // However in this case we need to take the responsibility for filtering children which
- // are relatives of another surface here.
- bool skipRelativeZUsers = false;
- const LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers);
-
- size_t i = 0;
- for (; i < list.size(); i++) {
- const auto& relative = list[i];
- if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) {
- continue;
- }
-
- if (relative->getZ(stateSet) >= 0) {
- break;
- }
- relative->traverseInZOrder(stateSet, visitor);
- }
-
- visitor(this);
- for (; i < list.size(); i++) {
- const auto& relative = list[i];
-
- if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) {
- continue;
- }
- relative->traverseInZOrder(stateSet, visitor);
- }
-}
-
-/**
- * Positively signed relatives are before 'this' in reverse Z-order.
- */
-void Layer::traverseInReverseZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor) {
- // See traverseInZOrder for documentation.
- bool skipRelativeZUsers = false;
- LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers);
-
- int32_t i = 0;
- for (i = int32_t(list.size()) - 1; i >= 0; i--) {
- const auto& relative = list[i];
-
- if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) {
- continue;
- }
-
- if (relative->getZ(stateSet) < 0) {
- break;
- }
- relative->traverseInReverseZOrder(stateSet, visitor);
- }
- visitor(this);
- for (; i >= 0; i--) {
- const auto& relative = list[i];
-
- if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) {
- continue;
- }
-
- relative->traverseInReverseZOrder(stateSet, visitor);
- }
-}
-
-void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
- visitor(this);
- const LayerVector& children =
- state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
- for (const sp<Layer>& child : children) {
- child->traverse(state, visitor);
- }
-}
-
-void Layer::traverseChildren(const LayerVector::Visitor& visitor) {
- for (const sp<Layer>& child : mDrawingChildren) {
- visitor(child.get());
- }
-}
-
-LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
- const std::vector<Layer*>& layersInTree) {
- LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
- "makeTraversalList received invalid stateSet");
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
-
- LayerVector traverse(stateSet);
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- // Only add relative layers that are also descendents of the top most parent of the tree.
- // If a relative layer is not a descendent, then it should be ignored.
- if (std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) {
- traverse.add(strongRelative);
- }
- }
-
- for (const sp<Layer>& child : children) {
- const State& childState = useDrawing ? child->mDrawingState : child->mDrawingState;
- // If a layer has a relativeOf layer, only ignore if the layer it's relative to is a
- // descendent of the top most parent of the tree. If it's not a descendent, then just add
- // the child here since it won't be added later as a relative.
- if (std::binary_search(layersInTree.begin(), layersInTree.end(),
- childState.zOrderRelativeOf.promote().get())) {
- continue;
- }
- traverse.add(child);
- }
-
- return traverse;
-}
-
-void Layer::traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree,
- LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor) {
- const LayerVector list = makeChildrenTraversalList(stateSet, layersInTree);
-
- size_t i = 0;
- for (; i < list.size(); i++) {
- const auto& relative = list[i];
- if (relative->getZ(stateSet) >= 0) {
- break;
- }
- relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
- }
-
- visitor(this);
- for (; i < list.size(); i++) {
- const auto& relative = list[i];
- relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
- }
-}
-
-std::vector<Layer*> Layer::getLayersInTree(LayerVector::StateSet stateSet) {
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
-
- std::vector<Layer*> layersInTree = {this};
- for (size_t i = 0; i < children.size(); i++) {
- const auto& child = children[i];
- std::vector<Layer*> childLayers = child->getLayersInTree(stateSet);
- layersInTree.insert(layersInTree.end(), childLayers.cbegin(), childLayers.cend());
- }
-
- return layersInTree;
-}
-
-void Layer::traverseChildrenInZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor) {
- std::vector<Layer*> layersInTree = getLayersInTree(stateSet);
- std::sort(layersInTree.begin(), layersInTree.end());
- traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
-}
-
-ui::Transform Layer::getTransform() const {
- return mEffectiveTransform;
-}
-
-bool Layer::isTransformValid() const {
- float transformDet = getTransform().det();
- return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
-}
-
-half Layer::getAlpha() const {
- const auto& p = mDrawingParent.promote();
-
- half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
- return parentAlpha * getDrawingState().color.a;
-}
-
-ui::Transform::RotationFlags Layer::getFixedTransformHint() const {
- ui::Transform::RotationFlags fixedTransformHint = mDrawingState.fixedTransformHint;
- if (fixedTransformHint != ui::Transform::ROT_INVALID) {
- return fixedTransformHint;
- }
- const auto& p = mCurrentParent.promote();
- if (!p) return fixedTransformHint;
- return p->getFixedTransformHint();
-}
-
-half4 Layer::getColor() const {
- const half4 color(getDrawingState().color);
- return half4(color.r, color.g, color.b, getAlpha());
-}
-
-int32_t Layer::getBackgroundBlurRadius() const {
- if (getDrawingState().backgroundBlurRadius == 0) {
- return 0;
- }
-
- const auto& p = mDrawingParent.promote();
- half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
- return parentAlpha * getDrawingState().backgroundBlurRadius;
-}
-
-const std::vector<BlurRegion> Layer::getBlurRegions() const {
- auto regionsCopy(getDrawingState().blurRegions);
- float layerAlpha = getAlpha();
- for (auto& region : regionsCopy) {
- region.alpha = region.alpha * layerAlpha;
- }
- return regionsCopy;
-}
-
-RoundedCornerState Layer::getRoundedCornerState() const {
- // Today's DPUs cannot do rounded corners. If RenderEngine cannot render
- // protected content, remove rounded corners from protected content so it
- // can be rendered by the DPU.
- if (isProtected() && !mFlinger->getRenderEngine().supportsProtectedContent()) {
- return {};
- }
-
- // Get parent settings
- RoundedCornerState parentSettings;
- const auto& parent = mDrawingParent.promote();
- if (parent != nullptr) {
- parentSettings = parent->getRoundedCornerState();
- if (parentSettings.hasRoundedCorners()) {
- ui::Transform t = getActiveTransform(getDrawingState());
- t = t.inverse();
- parentSettings.cropRect = t.transform(parentSettings.cropRect);
- parentSettings.radius.x *= t.getScaleX();
- parentSettings.radius.y *= t.getScaleY();
- }
- }
-
- // Get layer settings
- Rect layerCropRect = getCroppedBufferSize(getDrawingState());
- const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
- RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
- const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
-
- if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
- // If the parent and the layer have rounded corner settings, use the parent settings if the
- // parent crop is entirely inside the layer crop.
- // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
- if (parentSettings.cropRect.left > layerCropRect.left &&
- parentSettings.cropRect.top > layerCropRect.top &&
- parentSettings.cropRect.right < layerCropRect.right &&
- parentSettings.cropRect.bottom < layerCropRect.bottom) {
- return parentSettings;
- } else {
- return layerSettings;
- }
- } else if (layerSettingsValid) {
- return layerSettings;
- } else if (parentSettings.hasRoundedCorners()) {
- return parentSettings;
- }
- return {};
-}
-
-bool Layer::findInHierarchy(const sp<Layer>& l) {
- if (l == this) {
- return true;
- }
- for (auto& child : mDrawingChildren) {
- if (child->findInHierarchy(l)) {
- return true;
- }
- }
- return false;
-}
-
-void Layer::commitChildList() {
- for (size_t i = 0; i < mCurrentChildren.size(); i++) {
- const auto& child = mCurrentChildren[i];
- child->commitChildList();
- }
- mDrawingChildren = mCurrentChildren;
- mDrawingParent = mCurrentParent;
- if (CC_UNLIKELY(usingRelativeZ(LayerVector::StateSet::Drawing))) {
- auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
- if (zOrderRelativeOf == nullptr) return;
- if (findInHierarchy(zOrderRelativeOf)) {
- ALOGE("Detected Z ordering loop between %s and %s", mName.c_str(),
- zOrderRelativeOf->mName.c_str());
- ALOGE("Severing rel Z loop, potentially dangerous");
- mDrawingState.isRelativeOf = false;
- zOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this));
- }
- }
-}
-
-
-void Layer::setInputInfo(const WindowInfo& info) {
- mDrawingState.inputInfo = info;
- mDrawingState.touchableRegionCrop =
- LayerHandle::getLayer(info.touchableRegionCropHandle.promote());
- mDrawingState.modified = true;
- mFlinger->mUpdateInputInfo = true;
- setTransactionFlags(eTransactionNeeded);
-}
-
-perfetto::protos::LayerProto* Layer::writeToProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags) {
- perfetto::protos::LayerProto* layerProto = layersProto.add_layers();
- writeToProtoDrawingState(layerProto);
- writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
-
- if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
- ui::LayerStack layerStack =
- (mSnapshot) ? mSnapshot->outputFilter.layerStack : ui::INVALID_LAYER_STACK;
- writeCompositionStateToProto(layerProto, layerStack);
- }
-
- for (const sp<Layer>& layer : mDrawingChildren) {
- layer->writeToProto(layersProto, traceFlags);
- }
-
- return layerProto;
-}
-
void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto,
ui::LayerStack layerStack) {
ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
@@ -2206,383 +634,9 @@
}
}
-void Layer::writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo) {
- const ui::Transform transform = getTransform();
- auto buffer = getExternalTexture();
- if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(*buffer,
- [&]() { return layerInfo->mutable_active_buffer(); });
- LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
- layerInfo->mutable_buffer_transform());
- }
- layerInfo->set_invalidate(contentDirty);
- layerInfo->set_is_protected(isProtected());
- layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
- layerInfo->set_queued_frames(getQueuedFrameCount());
- layerInfo->set_curr_frame(mCurrentFrameNumber);
- layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(
- (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
- layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
- layerInfo->set_is_trusted_overlay(isTrustedOverlay());
- LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
- LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
- [&]() { return layerInfo->mutable_position(); });
- LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
- LayerProtoHelper::writeToProto(surfaceDamageRegion,
- [&]() { return layerInfo->mutable_damage_region(); });
-
- if (hasColorTransform()) {
- LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform());
- }
-
- LayerProtoHelper::writeToProto(mSourceBounds,
- [&]() { return layerInfo->mutable_source_bounds(); });
- LayerProtoHelper::writeToProto(mScreenBounds,
- [&]() { return layerInfo->mutable_screen_bounds(); });
- LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect,
- [&]() { return layerInfo->mutable_corner_radius_crop(); });
- layerInfo->set_shadow_radius(mEffectiveShadowRadius);
-}
-
-void Layer::writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo,
- LayerVector::StateSet stateSet, uint32_t traceFlags) {
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
-
- ui::Transform requestedTransform = state.transform;
-
- layerInfo->set_id(sequence);
- layerInfo->set_name(getName().c_str());
- layerInfo->set_type(getType());
-
- for (const auto& child : children) {
- layerInfo->add_children(child->sequence);
- }
-
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- layerInfo->add_relatives(strongRelative->sequence);
- }
- }
-
- LayerProtoHelper::writeToProto(state.transparentRegionHint,
- [&]() { return layerInfo->mutable_transparent_region(); });
-
- layerInfo->set_layer_stack(getLayerStack().id);
- layerInfo->set_z(state.z);
-
- LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
- return layerInfo->mutable_requested_position();
- });
-
- LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
-
- layerInfo->set_is_opaque(isOpaque(state));
-
- layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
- LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
- LayerProtoHelper::writeToProto(state.color,
- [&]() { return layerInfo->mutable_requested_color(); });
- layerInfo->set_flags(state.flags);
-
- LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
- layerInfo->mutable_requested_transform());
-
- auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent != nullptr) {
- layerInfo->set_parent(parent->sequence);
- }
-
- auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
- }
-
- layerInfo->set_is_relative_of(state.isRelativeOf);
-
- layerInfo->set_owner_uid(mOwnerUid);
-
- if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) {
- WindowInfo info;
- if (useDrawing) {
- info = fillInputInfo(
- InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true});
- } else {
- info = state.inputInfo;
- }
-
- LayerProtoHelper::writeToProto(info, state.touchableRegionCrop,
- [&]() { return layerInfo->mutable_input_window_info(); });
- }
-
- if (traceFlags & LayerTracing::TRACE_EXTRA) {
- auto protoMap = layerInfo->mutable_metadata();
- for (const auto& entry : state.metadata.mMap) {
- (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
- }
- }
-
- LayerProtoHelper::writeToProto(state.destinationFrame,
- [&]() { return layerInfo->mutable_destination_frame(); });
-}
-
-bool Layer::isRemovedFromCurrentState() const {
- return mRemovedFromDrawingState;
-}
-
-// Applies the given transform to the region, while protecting against overflows caused by any
-// offsets. If applying the offset in the transform to any of the Rects in the region would result
-// in an overflow, they are not added to the output Region.
-static Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r,
- const std::string& debugWindowName) {
- // Round the translation using the same rounding strategy used by ui::Transform.
- const auto tx = static_cast<int32_t>(t.tx() + 0.5);
- const auto ty = static_cast<int32_t>(t.ty() + 0.5);
-
- ui::Transform transformWithoutOffset = t;
- transformWithoutOffset.set(0.f, 0.f);
-
- const Region transformed = transformWithoutOffset.transform(r);
-
- // Apply the translation to each of the Rects in the region while discarding any that overflow.
- Region ret;
- for (const auto& rect : transformed) {
- Rect newRect;
- if (__builtin_add_overflow(rect.left, tx, &newRect.left) ||
- __builtin_add_overflow(rect.top, ty, &newRect.top) ||
- __builtin_add_overflow(rect.right, tx, &newRect.right) ||
- __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) {
- ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.",
- debugWindowName.c_str());
- continue;
- }
- ret.orSelf(newRect);
- }
- return ret;
-}
-
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
- auto [inputBounds, inputBoundsValid] = getInputBounds(/*fillParentBounds=*/false);
- if (!inputBoundsValid) {
- info.touchableRegion.clear();
- }
-
- info.frame = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay);
-
- ui::Transform inputToLayer;
- inputToLayer.set(inputBounds.left, inputBounds.top);
- const ui::Transform layerToScreen = getInputTransform();
- const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer;
-
- // InputDispatcher expects a display-to-input transform.
- info.transform = inputToDisplay.inverse();
-
- // The touchable region is specified in the input coordinate space. Change it to display space.
- info.touchableRegion =
- transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, mName);
-}
-
-void Layer::fillTouchOcclusionMode(WindowInfo& info) {
- sp<Layer> p = sp<Layer>::fromExisting(this);
- while (p != nullptr && !p->hasInputInfo()) {
- p = p->mDrawingParent.promote();
- }
- if (p != nullptr) {
- info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
- }
-}
-
-gui::DropInputMode Layer::getDropInputMode() const {
- gui::DropInputMode mode = mDrawingState.dropInputMode;
- if (mode == gui::DropInputMode::ALL) {
- return mode;
- }
- sp<Layer> parent = mDrawingParent.promote();
- if (parent) {
- gui::DropInputMode parentMode = parent->getDropInputMode();
- if (parentMode != gui::DropInputMode::NONE) {
- return parentMode;
- }
- }
- return mode;
-}
-
-void Layer::handleDropInputMode(gui::WindowInfo& info) const {
- if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
- return;
- }
-
- // Check if we need to drop input unconditionally
- gui::DropInputMode dropInputMode = getDropInputMode();
- if (dropInputMode == gui::DropInputMode::ALL) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- ALOGV("Dropping input for %s as requested by policy.", getDebugName());
- return;
- }
-
- // Check if we need to check if the window is obscured by parent
- if (dropInputMode != gui::DropInputMode::OBSCURED) {
- return;
- }
-
- // Check if the parent has set an alpha on the layer
- sp<Layer> parent = mDrawingParent.promote();
- if (parent && parent->getAlpha() != 1.0_hf) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
- static_cast<float>(getAlpha()));
- }
-
- // Check if the parent has cropped the buffer
- Rect bufferSize = getCroppedBufferSize(getDrawingState());
- if (!bufferSize.isValid()) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
- return;
- }
-
- // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
- // To check if the layer has been cropped, we take the buffer bounds, apply the local
- // layer crop and apply the same set of transforms to move to screenspace. If the bounds
- // match then the layer has not been cropped by its parents.
- Rect bufferInScreenSpace(getTransform().transform(bufferSize));
- bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
-
- if (croppedByParent) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
- getDebugName());
- } else {
- // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
- // input if the window is obscured. This check should be done in surfaceflinger but the
- // logic currently resides in inputflinger. So pass the if_obscured check to input to only
- // drop input events if the window is obscured.
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
- }
-}
-
-WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
- if (!hasInputInfo()) {
- mDrawingState.inputInfo.name = getName();
- mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid};
- mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid};
- mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
- mDrawingState.inputInfo.displayId = toLogicalDisplayId(getLayerStack());
- }
-
- const ui::Transform& displayTransform =
- displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform;
-
- WindowInfo info = mDrawingState.inputInfo;
- info.id = sequence;
- info.displayId = toLogicalDisplayId(getLayerStack());
-
- fillInputFrameInfo(info, displayTransform);
-
- if (displayArgs.transform == nullptr) {
- // Do not let the window receive touches if it is not associated with a valid display
- // transform. We still allow the window to receive keys and prevent ANRs.
- info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
- }
-
- info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput());
-
- info.alpha = getAlpha();
- fillTouchOcclusionMode(info);
- handleDropInputMode(info);
-
- // If the window will be blacked out on a display because the display does not have the secure
- // flag and the layer has the secure flag set, then drop input.
- if (!displayArgs.isSecure && isSecure()) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- }
-
- sp<Layer> cropLayer = mDrawingState.touchableRegionCrop.promote();
- if (info.replaceTouchableRegionWithCrop) {
- Rect inputBoundsInDisplaySpace;
- if (!cropLayer) {
- FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first;
- inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform);
- } else {
- FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
- inputBoundsInDisplaySpace =
- cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
- }
- info.touchableRegion = Region(inputBoundsInDisplaySpace);
- } else if (cropLayer != nullptr) {
- FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
- Rect inputBoundsInDisplaySpace =
- cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
- info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace);
- }
-
- // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
- // if it was set by WM for a known system overlay
- if (isTrustedOverlay()) {
- info.inputConfig |= WindowInfo::InputConfig::TRUSTED_OVERLAY;
- }
-
- // If the layer is a clone, we need to crop the input region to cloned root to prevent
- // touches from going outside the cloned area.
- if (isClone()) {
- info.inputConfig |= WindowInfo::InputConfig::CLONE;
- if (const sp<Layer> clonedRoot = getClonedRoot()) {
- const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds});
- info.touchableRegion = info.touchableRegion.intersect(rect);
- }
- }
-
- Rect bufferSize = getBufferSize(getDrawingState());
- info.contentSize = Size(bufferSize.width(), bufferSize.height());
-
- return info;
-}
-
-Rect Layer::getInputBoundsInDisplaySpace(const FloatRect& inputBounds,
- const ui::Transform& screenToDisplay) {
- // InputDispatcher works in the display device's coordinate space. Here, we calculate the
- // frame and transform used for the layer, which determines the bounds and the coordinate space
- // within which the layer will receive input.
-
- // Coordinate space definitions:
- // - display: The display device's coordinate space. Correlates to pixels on the display.
- // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
- // - layer: The coordinate space of this layer.
- // - input: The coordinate space in which this layer will receive input events. This could be
- // different than layer space if a surfaceInset is used, which changes the origin
- // of the input space.
-
- // Crop the input bounds to ensure it is within the parent's bounds.
- const FloatRect croppedInputBounds = mBounds.intersect(inputBounds);
- const ui::Transform layerToScreen = getInputTransform();
- const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
- return Rect{layerToDisplay.transform(croppedInputBounds)};
-}
-
-sp<Layer> Layer::getClonedRoot() {
- if (mClonedChild != nullptr) {
- return sp<Layer>::fromExisting(this);
- }
- if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
- return nullptr;
- }
- return mDrawingParent.promote()->getClonedRoot();
-}
-
-bool Layer::hasInputInfo() const {
- return mDrawingState.inputInfo.token != nullptr ||
- mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
-}
-
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
const DisplayDevice* display) const {
if (!display) return nullptr;
- if (!mFlinger->mLayerLifecycleManagerEnabled) {
- return display->getCompositionDisplay()->getOutputLayerForLayer(
- getCompositionEngineLayerFE());
- }
sp<LayerFE> layerFE;
frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)};
for (auto& [p, layer] : mLayerFEs) {
@@ -2598,10 +652,6 @@
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const {
if (!display) return nullptr;
- if (!mFlinger->mLayerLifecycleManagerEnabled) {
- return display->getCompositionDisplay()->getOutputLayerForLayer(
- getCompositionEngineLayerFE());
- }
sp<LayerFE> layerFE;
for (auto& [p, layer] : mLayerFEs) {
if (p == path) {
@@ -2618,215 +668,27 @@
return outputLayer ? outputLayer->getState().visibleRegion : Region();
}
-void Layer::updateCloneBufferInfo() {
- if (!isClone() || !isClonedFromAlive()) {
- return;
- }
-
- sp<Layer> clonedFrom = getClonedFrom();
- mBufferInfo = clonedFrom->mBufferInfo;
- mSidebandStream = clonedFrom->mSidebandStream;
- surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
- mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
- mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
-
- // After buffer info is updated, the drawingState from the real layer needs to be copied into
- // the cloned. This is because some properties of drawingState can change when latchBuffer is
- // called. However, copying the drawingState would also overwrite the cloned layer's relatives
- // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
- // the cloned drawingState again.
- wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
- SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
- wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
- WindowInfo tmpInputInfo = mDrawingState.inputInfo;
-
- cloneDrawingState(clonedFrom.get());
-
- mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
- mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
- mDrawingState.zOrderRelatives = tmpZOrderRelatives;
- mDrawingState.inputInfo = tmpInputInfo;
-}
-
-bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) {
- if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
- // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
- // it means that there is a clone, but the layer it was cloned from has been destroyed. In
- // that case, we want to delete the reference to the clone since we want it to get
- // destroyed. The root, this layer, will still be around since the client can continue
- // to hold a reference, but no cloned layers will be displayed.
- mClonedChild = nullptr;
- return true;
- }
-
- std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
- // If the real layer exists and is in current state, add the clone as a child of the root.
- // There's no need to remove from drawingState when the layer is offscreen since currentState is
- // copied to drawingState for the root layer. So the clonedChild is always removed from
- // drawingState and then needs to be added back each traversal.
- if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) {
- addChildToDrawing(mClonedChild);
- }
-
- mClonedChild->updateClonedDrawingState(clonedLayersMap);
- mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap);
- mClonedChild->updateClonedRelatives(clonedLayersMap);
-
- for (Layer* root : cloneRootsPendingUpdates) {
- if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) {
- return false;
- }
- }
- return true;
-}
-
-void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
- // If the layer the clone was cloned from is alive, copy the content of the drawingState
- // to the clone. If the real layer is no longer alive, continue traversing the children
- // since we may be able to pull out other children that are still alive.
- if (isClonedFromAlive()) {
- sp<Layer> clonedFrom = getClonedFrom();
- cloneDrawingState(clonedFrom.get());
- clonedLayersMap.emplace(clonedFrom, sp<Layer>::fromExisting(this));
- }
-
- // The clone layer may have children in drawingState since they may have been created and
- // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones
- // that already exist, since we can just re-use them.
- // The drawingChildren will not get overwritten by the currentChildren since the clones are
- // not updated in the regular traversal. They are skipped since the root will lose the
- // reference to them when it copies its currentChildren to drawing.
- for (sp<Layer>& child : mDrawingChildren) {
- child->updateClonedDrawingState(clonedLayersMap);
- }
-}
-
-void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot,
- std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
- mDrawingChildren.clear();
-
- if (!isClonedFromAlive()) {
- return;
- }
-
- sp<Layer> clonedFrom = getClonedFrom();
- for (sp<Layer>& child : clonedFrom->mDrawingChildren) {
- if (child == mirrorRoot) {
- // This is to avoid cyclical mirroring.
- continue;
- }
- sp<Layer> clonedChild = clonedLayersMap[child];
- if (clonedChild == nullptr) {
- clonedChild = child->createClone();
- clonedLayersMap[child] = clonedChild;
- }
- addChildToDrawing(clonedChild);
- clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap);
- }
-}
-
-void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
- auto cropLayer = mDrawingState.touchableRegionCrop.promote();
- if (cropLayer != nullptr) {
- if (clonedLayersMap.count(cropLayer) == 0) {
- // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to
- // self as crop layer to avoid going outside bounds.
- mDrawingState.touchableRegionCrop = wp<Layer>::fromExisting(this);
- } else {
- const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer);
- mDrawingState.touchableRegionCrop = clonedCropLayer;
- }
- }
- // Cloned layers shouldn't handle watch outside since their z order is not determined by
- // WM or the client.
- mDrawingState.inputInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, false);
-}
-
-void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
- mDrawingState.zOrderRelativeOf = wp<Layer>();
- mDrawingState.zOrderRelatives.clear();
-
- if (!isClonedFromAlive()) {
- return;
- }
-
- const sp<Layer>& clonedFrom = getClonedFrom();
- for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
- const sp<Layer>& relative = relativeWeak.promote();
- if (clonedLayersMap.count(relative) > 0) {
- auto& clonedRelative = clonedLayersMap.at(relative);
- mDrawingState.zOrderRelatives.add(clonedRelative);
- }
- }
-
- // Check if the relativeLayer for the real layer is part of the cloned hierarchy.
- // It's possible that the layer it's relative to is outside the requested cloned hierarchy.
- // In that case, we treat the layer as if the relativeOf has been removed. This way, it will
- // still traverse the children, but the layer with the missing relativeOf will not be shown
- // on screen.
- const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
- if (clonedLayersMap.count(relativeOf) > 0) {
- const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf);
- mDrawingState.zOrderRelativeOf = clonedRelativeOf;
- }
-
- updateClonedInputInfo(clonedLayersMap);
-
- for (sp<Layer>& child : mDrawingChildren) {
- child->updateClonedRelatives(clonedLayersMap);
- }
-}
-
-void Layer::addChildToDrawing(const sp<Layer>& layer) {
- mDrawingChildren.add(layer);
- layer->mDrawingParent = sp<Layer>::fromExisting(this);
-}
-
-bool Layer::isInternalDisplayOverlay() const {
- const State& s(mDrawingState);
- if (s.flags & layer_state_t::eLayerSkipScreenshot) {
- return true;
- }
-
- sp<Layer> parent = mDrawingParent.promote();
- return parent && parent->isInternalDisplayOverlay();
-}
-
-void Layer::setClonedChild(const sp<Layer>& clonedChild) {
- mClonedChild = clonedChild;
- mHadClonedChild = true;
- mFlinger->mLayerMirrorRoots.push_back(this);
-}
-
-bool Layer::setDropInputMode(gui::DropInputMode mode) {
- if (mDrawingState.dropInputMode == mode) {
- return false;
- }
- mDrawingState.dropInputMode = mode;
- return true;
-}
-
-void Layer::cloneDrawingState(const Layer* from) {
- mDrawingState = from->mDrawingState;
- // Skip callback info since they are not applicable for cloned layers.
- mDrawingState.releaseBufferListener = nullptr;
- // TODO (b/238781169) currently broken for mirror layers because we do not
- // track release fences for mirror layers composed on other displays
- mDrawingState.callbackHandles = {};
-}
-
void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence) {
- if (!listener) {
+ if (!listener && !mBufferReleaseChannel) {
return;
}
- ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
+
+ SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
+
+ ReleaseCallbackId callbackId{buffer->getId(), framenumber};
+ const sp<Fence>& fence = releaseFence ? releaseFence : Fence::NO_FENCE;
uint32_t currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
- listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+
+ if (listener) {
+ listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
+ }
+
+ if (mBufferReleaseChannel) {
+ mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount);
+ }
}
sp<CallbackHandle> Layer::findCallbackHandle() {
@@ -2942,33 +804,15 @@
}
}
-void Layer::onSurfaceFrameCreated(
- const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
- while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
- // Too many SurfaceFrames pending classification. The front of the deque is probably not
- // tracked by FrameTimeline and will never be presented. This will only result in a memory
- // leak.
- if (hasBufferOrSidebandStreamInDrawing()) {
- // Only log for layers with a buffer, since we expect the jank data to be drained for
- // these, while there may be no jank listeners for bufferless layers.
- ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
- mName.c_str());
- std::string miniDump = mPendingJankClassifications.front()->miniDump();
- ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
- }
- mPendingJankClassifications.pop_front();
- }
- mPendingJankClassifications.emplace_back(surfaceFrame);
-}
-
void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->bufferReleaseChannel = mBufferReleaseChannel;
handle->transformHint = mTransformHint;
handle->dequeueReadyTime = dequeueReadyTime;
handle->currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
- ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
- handle->previousReleaseCallbackId.framenumber);
+ SFTRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
+ handle->previousReleaseCallbackId.framenumber);
}
for (auto& handle : mDrawingState.callbackHandles) {
@@ -2978,24 +822,13 @@
}
}
- std::vector<JankData> jankData;
- transferAvailableJankData(mDrawingState.callbackHandles, jankData);
- mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles,
- jankData);
+ mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles);
mDrawingState.callbackHandles = {};
}
-bool Layer::willPresentCurrentTransaction() const {
- // Returns true if the most recent Transaction applied to CurrentState will be presented.
- return (getSidebandStreamChanged() || getAutoRefresh() ||
- (mDrawingState.modified &&
- (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
-}
-
bool Layer::setTransform(uint32_t transform) {
if (mDrawingState.bufferTransform == transform) return false;
mDrawingState.bufferTransform = transform;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -3004,111 +837,10 @@
if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
mDrawingState.sequence++;
mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool Layer::setBufferCrop(const Rect& bufferCrop) {
- if (mDrawingState.bufferCrop == bufferCrop) return false;
-
- mDrawingState.sequence++;
- mDrawingState.bufferCrop = bufferCrop;
-
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setDestinationFrame(const Rect& destinationFrame) {
- if (mDrawingState.destinationFrame == destinationFrame) return false;
-
- mDrawingState.sequence++;
- mDrawingState.destinationFrame = destinationFrame;
-
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-// Translate destination frame into scale and position. If a destination frame is not set, use the
-// provided scale and position
-bool Layer::updateGeometry() {
- if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
- mDrawingState.destinationFrame.isEmpty()) {
- // If destination frame is not set, use the requested transform set via
- // Layer::setPosition and Layer::setMatrix.
- return assignTransform(&mDrawingState.transform, mRequestedTransform);
- }
-
- Rect destRect = mDrawingState.destinationFrame;
- int32_t destW = destRect.width();
- int32_t destH = destRect.height();
- if (destRect.left < 0) {
- destRect.left = 0;
- destRect.right = destW;
- }
- if (destRect.top < 0) {
- destRect.top = 0;
- destRect.bottom = destH;
- }
-
- if (!mDrawingState.buffer) {
- ui::Transform t;
- t.set(destRect.left, destRect.top);
- return assignTransform(&mDrawingState.transform, t);
- }
-
- uint32_t bufferWidth = mDrawingState.buffer->getWidth();
- uint32_t bufferHeight = mDrawingState.buffer->getHeight();
- // Undo any transformations on the buffer.
- if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
- if (mDrawingState.transformToDisplayInverse) {
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- }
-
- float sx = destW / static_cast<float>(bufferWidth);
- float sy = destH / static_cast<float>(bufferHeight);
- ui::Transform t;
- t.set(sx, 0, 0, sy);
- t.set(destRect.left, destRect.top);
- return assignTransform(&mDrawingState.transform, t);
-}
-
-bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
- if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
- mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
- return false;
- }
-
- mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- return true;
-}
-
-bool Layer::setPosition(float x, float y) {
- if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
- return false;
- }
-
- mRequestedTransform.set(x, y);
-
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- return true;
-}
-
void Layer::releasePreviousBuffer() {
mReleasePreviousBuffer = true;
if (!mBufferInfo.mBuffer ||
@@ -3151,14 +883,14 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, const FrameTimelineInfo& info) {
- ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
+ bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) {
+ SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
const bool frameNumberChanged =
bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
const uint64_t frameNumber =
frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
- ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber);
+ SFTRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber);
if (mDrawingState.buffer) {
releasePreviousBuffer();
@@ -3172,17 +904,16 @@
mDrawingState.isAutoTimestamp = isAutoTimestamp;
mDrawingState.latchedVsyncId = info.vsyncId;
mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection;
- mDrawingState.modified = true;
if (!buffer) {
resetDrawingStateBufferInfo();
setTransactionFlags(eTransactionNeeded);
mDrawingState.bufferSurfaceFrameTX = nullptr;
- setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+ setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode);
return true;
} else {
// release sideband stream if it exists and a non null buffer is being set
if (mDrawingState.sidebandStream != nullptr) {
- setSidebandStream(nullptr, info, postTime);
+ setSidebandStream(nullptr, info, postTime, gameMode);
}
}
@@ -3221,9 +952,9 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
- mOwnerUid, postTime, getGameMode());
+ mOwnerUid, postTime, gameMode);
- setFrameTimelineVsyncForBufferTransaction(info, postTime);
+ setFrameTimelineVsyncForBufferTransaction(info, postTime, gameMode);
if (bufferData.dequeueTime > 0) {
const uint64_t bufferId = mDrawingState.buffer->getId();
@@ -3253,10 +984,10 @@
}
void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps, nsecs_t now) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const nsecs_t presentTime = [&] {
if (!mDrawingState.isAutoTimestamp) {
- ATRACE_FORMAT_INSTANT("desiredPresentTime");
+ SFTRACE_FORMAT_INSTANT("desiredPresentTime");
return mDrawingState.desiredPresentTime;
}
@@ -3265,7 +996,7 @@
mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
mDrawingState.latchedVsyncId);
if (prediction.has_value()) {
- ATRACE_FORMAT_INSTANT("predictedPresentTime");
+ SFTRACE_FORMAT_INSTANT("predictedPresentTime");
mMaxTimeForUseVsyncId = prediction->presentTime +
scheduler::LayerHistory::kMaxPeriodForHistory.count();
return prediction->presentTime;
@@ -3301,9 +1032,9 @@
return static_cast<nsecs_t>(0);
}();
- if (ATRACE_ENABLED() && presentTime > 0) {
+ if (SFTRACE_ENABLED() && presentTime > 0) {
const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now();
- ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
+ SFTRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
}
mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
@@ -3320,7 +1051,6 @@
bool Layer::setDataspace(ui::Dataspace dataspace) {
if (mDrawingState.dataspace == dataspace) return false;
mDrawingState.dataspace = dataspace;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -3331,7 +1061,6 @@
return false;
mDrawingState.currentHdrSdrRatio = currentBufferRatio;
mDrawingState.desiredHdrSdrRatio = desiredRatio;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -3339,46 +1068,12 @@
bool Layer::setDesiredHdrHeadroom(float desiredRatio) {
if (mDrawingState.desiredHdrSdrRatio == desiredRatio) return false;
mDrawingState.desiredHdrSdrRatio = desiredRatio;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setCachingHint(gui::CachingHint cachingHint) {
- if (mDrawingState.cachingHint == cachingHint) return false;
- mDrawingState.cachingHint = cachingHint;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
- if (mDrawingState.hdrMetadata == hdrMetadata) return false;
- mDrawingState.hdrMetadata = hdrMetadata;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
- if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
- mDrawingState.surfaceDamageRegion = surfaceDamage;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- setIsSmallDirty(surfaceDamage, getTransform());
- return true;
-}
-
-bool Layer::setApi(int32_t api) {
- if (mDrawingState.api == api) return false;
- mDrawingState.api = api;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info,
- nsecs_t postTime) {
+ nsecs_t postTime, gui::GameMode gameMode) {
if (mDrawingState.sidebandStream == sidebandStream) return false;
if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
@@ -3388,12 +1083,11 @@
}
mDrawingState.sidebandStream = sidebandStream;
- mDrawingState.modified = true;
if (sidebandStream != nullptr && mDrawingState.buffer != nullptr) {
releasePreviousBuffer();
resetDrawingStateBufferInfo();
mDrawingState.bufferSurfaceFrameTX = nullptr;
- setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+ setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode);
}
setTransactionFlags(eTransactionNeeded);
if (!mSidebandStreamChanged.exchange(true)) {
@@ -3451,9 +1145,7 @@
if (!remainingHandles.empty()) {
// Notify the transaction completed threads these handles are done. These are only the
// handles that were not added to the mDrawingState, which will be notified later.
- std::vector<JankData> jankData;
- transferAvailableJankData(remainingHandles, jankData);
- mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles, jankData);
+ mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles);
}
mReleasePreviousBuffer = false;
@@ -3487,14 +1179,6 @@
return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
}
-FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const {
- if (mBufferInfo.mBuffer == nullptr) {
- return parentBounds;
- }
-
- return getBufferSize(getDrawingState()).toFloatRect();
-}
-
bool Layer::fenceHasSignaled() const {
if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
@@ -3516,37 +1200,21 @@
}
}
-void Layer::setAutoRefresh(bool autoRefresh) {
- mDrawingState.autoRefresh = autoRefresh;
-}
-
bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) {
- // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
- auto* snapshot = editLayerSnapshot();
- snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
-
if (mSidebandStreamChanged.exchange(false)) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
mSidebandStream = s.sidebandStream;
- snapshot->sidebandStream = mSidebandStream;
if (mSidebandStream != nullptr) {
setTransactionFlags(eTransactionNeeded);
mFlinger->setTransactionFlags(eTraversalNeeded);
}
recomputeVisibleRegions = true;
-
return true;
}
return false;
}
-bool Layer::hasFrameUpdate() const {
- const State& c(getDrawingState());
- return (mDrawingStateModified || mDrawingState.modified) &&
- (c.buffer != nullptr || c.bgColorLayer != nullptr);
-}
-
void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
const State& s(getDrawingState());
@@ -3593,8 +1261,6 @@
mFlinger->getTransactionCallbackInvoker()
.addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
mDrawingState.callbackHandles = remainingHandles;
-
- mDrawingStateModified = false;
}
void Layer::gatherBufferInfo() {
@@ -3618,7 +1284,6 @@
mBufferInfo.mFrameLatencyNeeded = true;
mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
- mBufferInfo.mFence = mDrawingState.acquireFence;
mBufferInfo.mTransform = mDrawingState.bufferTransform;
auto lastDataspace = mBufferInfo.mDataspace;
mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
@@ -3629,12 +1294,12 @@
ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
status_t err = OK;
{
- ATRACE_NAME("getDataspace");
+ SFTRACE_NAME("getDataspace");
err = mapper.getDataspace(mBufferInfo.mBuffer->getBuffer()->handle, &dataspace);
}
if (err != OK || dataspace != mBufferInfo.mDataspace) {
{
- ATRACE_NAME("setDataspace");
+ SFTRACE_NAME("setDataspace");
err = mapper.setDataspace(mBufferInfo.mBuffer->getBuffer()->handle,
static_cast<ui::Dataspace>(mBufferInfo.mDataspace));
}
@@ -3666,10 +1331,6 @@
mFlinger->mHdrLayerInfoChanged = true;
}
mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
- mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
- mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
- mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
- mBufferInfo.mApi = mDrawingState.api;
mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
}
@@ -3685,308 +1346,13 @@
}
}
-sp<Layer> Layer::createClone() {
- surfaceflinger::LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0,
- LayerMetadata());
- sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
- return layer;
-}
-
void Layer::decrementPendingBufferCount() {
int32_t pendingBuffers = --mPendingBufferTransactions;
tracePendingBufferCount(pendingBuffers);
}
void Layer::tracePendingBufferCount(int32_t pendingBuffers) {
- ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
-}
-
-/*
- * We don't want to send the layer's transform to input, but rather the
- * parent's transform. This is because Layer's transform is
- * information about how the buffer is placed on screen. The parent's
- * transform makes more sense to send since it's information about how the
- * layer is placed on screen. This transform is used by input to determine
- * how to go from screen space back to window space.
- */
-ui::Transform Layer::getInputTransform() const {
- if (!hasBufferOrSidebandStream()) {
- return getTransform();
- }
- sp<Layer> parent = mDrawingParent.promote();
- if (parent == nullptr) {
- return ui::Transform();
- }
-
- return parent->getTransform();
-}
-
-/**
- * Returns the bounds used to fill the input frame and the touchable region.
- *
- * Similar to getInputTransform, we need to update the bounds to include the transform.
- * This is because bounds don't include the buffer transform, where the input assumes
- * that's already included.
- */
-std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const {
- Rect croppedBufferSize = getCroppedBufferSize(getDrawingState());
- FloatRect inputBounds = croppedBufferSize.toFloatRect();
- if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() &&
- mDrawingState.transform.getType() != ui::Transform::IDENTITY) {
- inputBounds = mDrawingState.transform.transform(inputBounds);
- }
-
- bool inputBoundsValid = croppedBufferSize.isValid();
- if (!inputBoundsValid) {
- /**
- * Input bounds are based on the layer crop or buffer size. But if we are using
- * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
- * we can use the parent bounds as the input bounds if the layer does not have buffer
- * or a crop. We want to unify this logic but because of compat reasons we cannot always
- * use the parent bounds. A layer without a buffer can get input. So when a window is
- * initially added, its touchable region can fill its parent layer bounds and that can
- * have negative consequences.
- */
- inputBounds = fillParentBounds ? mBounds : FloatRect{};
- }
-
- // Clamp surface inset to the input bounds.
- const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset);
- const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
- const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);
-
- // Apply the insets to the input bounds.
- inputBounds.left += xSurfaceInset;
- inputBounds.top += ySurfaceInset;
- inputBounds.right -= xSurfaceInset;
- inputBounds.bottom -= ySurfaceInset;
-
- return {inputBounds, inputBoundsValid};
-}
-
-bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const {
- const uint64_t requiredFlags = layer_state_t::eBufferChanged;
-
- const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
- layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
- layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
- layer_state_t::eLayerStackChanged | layer_state_t::eReparent |
- (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
- ? 0
- : layer_state_t::eAutoRefreshChanged);
-
- if ((s.what & requiredFlags) != requiredFlags) {
- ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
- (s.what | requiredFlags) & ~s.what);
- return false;
- }
-
- if (s.what & deniedFlags) {
- ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
- s.what & deniedFlags);
- return false;
- }
-
- if (s.what & layer_state_t::ePositionChanged) {
- if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
- ATRACE_FORMAT_INSTANT("%s: false [ePositionChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eAlphaChanged) {
- if (mDrawingState.color.a != s.color.a) {
- ATRACE_FORMAT_INSTANT("%s: false [eAlphaChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eColorTransformChanged) {
- if (mDrawingState.colorTransform != s.colorTransform) {
- ATRACE_FORMAT_INSTANT("%s: false [eColorTransformChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBackgroundColorChanged) {
- if (mDrawingState.bgColorLayer || s.bgColor.a != 0) {
- ATRACE_FORMAT_INSTANT("%s: false [eBackgroundColorChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eMatrixChanged) {
- if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
- mRequestedTransform.dtdy() != s.matrix.dtdy ||
- mRequestedTransform.dtdx() != s.matrix.dtdx ||
- mRequestedTransform.dsdy() != s.matrix.dsdy) {
- ATRACE_FORMAT_INSTANT("%s: false [eMatrixChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eCornerRadiusChanged) {
- if (mDrawingState.cornerRadius != s.cornerRadius) {
- ATRACE_FORMAT_INSTANT("%s: false [eCornerRadiusChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
- if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
- ATRACE_FORMAT_INSTANT("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBufferTransformChanged) {
- if (mDrawingState.bufferTransform != s.bufferTransform) {
- ATRACE_FORMAT_INSTANT("%s: false [eBufferTransformChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
- if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
- ATRACE_FORMAT_INSTANT("%s: false [eTransformToDisplayInverseChanged changed]",
- __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eCropChanged) {
- if (mDrawingState.crop != s.crop) {
- ATRACE_FORMAT_INSTANT("%s: false [eCropChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDataspaceChanged) {
- if (mDrawingState.dataspace != s.dataspace) {
- ATRACE_FORMAT_INSTANT("%s: false [eDataspaceChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eHdrMetadataChanged) {
- if (mDrawingState.hdrMetadata != s.hdrMetadata) {
- ATRACE_FORMAT_INSTANT("%s: false [eHdrMetadataChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eSidebandStreamChanged) {
- if (mDrawingState.sidebandStream != s.sidebandStream) {
- ATRACE_FORMAT_INSTANT("%s: false [eSidebandStreamChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
- if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
- ATRACE_FORMAT_INSTANT("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eShadowRadiusChanged) {
- if (mDrawingState.shadowRadius != s.shadowRadius) {
- ATRACE_FORMAT_INSTANT("%s: false [eShadowRadiusChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eFixedTransformHintChanged) {
- if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
- ATRACE_FORMAT_INSTANT("%s: false [eFixedTransformHintChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eTrustedOverlayChanged) {
- if (mDrawingState.isTrustedOverlay != (s.trustedOverlay == gui::TrustedOverlay::ENABLED)) {
- ATRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eStretchChanged) {
- StretchEffect temp = s.stretchEffect;
- temp.sanitize();
- if (mDrawingState.stretchEffect != temp) {
- ATRACE_FORMAT_INSTANT("%s: false [eStretchChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBufferCropChanged) {
- if (mDrawingState.bufferCrop != s.bufferCrop) {
- ATRACE_FORMAT_INSTANT("%s: false [eBufferCropChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDestinationFrameChanged) {
- if (mDrawingState.destinationFrame != s.destinationFrame) {
- ATRACE_FORMAT_INSTANT("%s: false [eDestinationFrameChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDimmingEnabledChanged) {
- if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
- ATRACE_FORMAT_INSTANT("%s: false [eDimmingEnabledChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
- if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio ||
- mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
- ATRACE_FORMAT_INSTANT("%s: false [eExtendedRangeBrightnessChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDesiredHdrHeadroomChanged) {
- if (mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
- ATRACE_FORMAT_INSTANT("%s: false [eDesiredHdrHeadroomChanged changed]", __func__);
- return false;
- }
- }
-
- return true;
-}
-
-sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
- // There's no need to get a CE Layer if the layer isn't going to draw anything.
- return hasSomethingToDraw() ? mLegacyLayerFE : nullptr;
-}
-
-const LayerSnapshot* Layer::getLayerSnapshot() const {
- return mSnapshot.get();
-}
-
-LayerSnapshot* Layer::editLayerSnapshot() {
- return mSnapshot.get();
-}
-
-std::unique_ptr<frontend::LayerSnapshot> Layer::stealLayerSnapshot() {
- return std::move(mSnapshot);
-}
-
-void Layer::updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot) {
- mSnapshot = std::move(snapshot);
-}
-
-const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
- return mSnapshot.get();
-}
-
-sp<LayerFE> Layer::copyCompositionEngineLayerFE() const {
- auto result = mFlinger->getFactory().createLayerFE(mName, this);
- result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot);
- return result;
+ SFTRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
}
sp<LayerFE> Layer::getCompositionEngineLayerFE(
@@ -4001,59 +1367,11 @@
return layerFE;
}
-void Layer::useSurfaceDamage() {
- if (mFlinger->mForceFullDamage) {
- surfaceDamageRegion = Region::INVALID_REGION;
- } else {
- surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
- }
-}
-
-void Layer::useEmptyDamage() {
- surfaceDamageRegion.clear();
-}
-
-bool Layer::isOpaque(const Layer::State& s) const {
- // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
- // layer's opaque flag.
- if (!hasSomethingToDraw()) {
- return false;
- }
-
- // if the layer has the opaque flag, then we're always opaque
- if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) {
- return true;
- }
-
- // If the buffer has no alpha channel, then we are opaque
- if (hasBufferOrSidebandStream() && LayerSnapshot::isOpaqueFormat(getPixelFormat())) {
- return true;
- }
-
- // Lastly consider the layer opaque if drawing a color with alpha == 1.0
- return fillsColor() && getAlpha() == 1.0_hf;
-}
-
-bool Layer::canReceiveInput() const {
- return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
-}
-
-bool Layer::isVisible() const {
- if (!hasSomethingToDraw()) {
- return false;
- }
-
- if (isHiddenByPolicy()) {
- return false;
- }
-
- return getAlpha() > 0.0f || hasBlur();
-}
-
void Layer::onCompositionPresented(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming& compositorTiming) {
+ const CompositorTiming& compositorTiming,
+ gui::GameMode gameMode) {
// mFrameLatencyNeeded is true when a new frame was latched for the
// composition.
if (!mBufferInfo.mFrameLatencyNeeded) return;
@@ -4095,12 +1413,12 @@
}
if (display) {
- const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
+ const auto activeMode = display->refreshRateSelector().getActiveMode();
+ const Fps refreshRate = activeMode.fps;
const std::optional<Fps> renderRate =
mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
const auto vote = frameRateToSetFrameRateVotePayload(getFrameRateForLayerTree());
- const auto gameMode = getGameMode();
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
@@ -4116,7 +1434,12 @@
mFlinger->getHwComposer().getPresentTimestamp(*displayId);
const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+ const nsecs_t vsyncPeriod =
+ mFlinger->getHwComposer()
+ .getDisplayVsyncPeriod(*displayId)
+ .value_opt()
+ .value_or(activeMode.modePtr->getVsyncRate().getPeriodNsecs());
+
const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
@@ -4132,18 +1455,9 @@
mBufferInfo.mFrameLatencyNeeded = false;
}
-bool Layer::willReleaseBufferOnLatch() const {
- return !mDrawingState.buffer && mBufferInfo.mBuffer;
-}
-
-bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
- const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
- return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
-}
-
bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
- ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
- getDrawingState().frameNumber);
+ SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
+ getDrawingState().frameNumber);
bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
@@ -4154,7 +1468,7 @@
// If the head buffer's acquire fence hasn't signaled yet, return and
// try again later
if (!fenceHasSignaled()) {
- ATRACE_NAME("!fenceHasSignaled()");
+ SFTRACE_NAME("!fenceHasSignaled()");
mFlinger->onLayerUpdate();
return false;
}
@@ -4162,7 +1476,6 @@
// Capture the old state of the layer for comparisons later
BufferInfo oldBufferInfo = mBufferInfo;
- const bool oldOpacity = isOpaque(mDrawingState);
mPreviousFrameNumber = mCurrentFrameNumber;
mCurrentFrameNumber = mDrawingState.frameNumber;
gatherBufferInfo();
@@ -4187,7 +1500,6 @@
if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
(mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
- (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
(mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
recomputeVisibleRegions = true;
}
@@ -4200,70 +1512,13 @@
recomputeVisibleRegions = true;
}
}
-
- if (oldOpacity != isOpaque(mDrawingState)) {
- recomputeVisibleRegions = true;
- }
-
return true;
}
-bool Layer::hasReadyFrame() const {
- return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
-}
-
-bool Layer::isProtected() const {
- return (mBufferInfo.mBuffer != nullptr) &&
- (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-void Layer::latchAndReleaseBuffer() {
- if (hasReadyFrame()) {
- bool ignored = false;
- latchBuffer(ignored, systemTime());
- }
- releasePendingBuffer(systemTime());
-}
-
-PixelFormat Layer::getPixelFormat() const {
- return mBufferInfo.mPixelFormat;
-}
-
bool Layer::getTransformToDisplayInverse() const {
return mBufferInfo.mTransformToDisplayInverse;
}
-Rect Layer::getBufferCrop() const {
- // this is the crop rectangle that applies to the buffer
- // itself (as opposed to the window)
- if (!mBufferInfo.mCrop.isEmpty()) {
- // if the buffer crop is defined, we use that
- return mBufferInfo.mCrop;
- } else if (mBufferInfo.mBuffer != nullptr) {
- // otherwise we use the whole buffer
- return mBufferInfo.mBuffer->getBounds();
- } else {
- // if we don't have a buffer yet, we use an empty/invalid crop
- return Rect();
- }
-}
-
-uint32_t Layer::getBufferTransform() const {
- return mBufferInfo.mTransform;
-}
-
-ui::Dataspace Layer::getDataSpace() const {
- return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
-}
-
-bool Layer::isFrontBuffered() const {
- if (mBufferInfo.mBuffer == nullptr) {
- return false;
- }
-
- return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
-}
-
ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
ui::Dataspace updatedDataspace = dataspace;
// translate legacy dataspaces to modern dataspaces
@@ -4299,123 +1554,6 @@
return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
}
-void Layer::setTransformHintLegacy(ui::Transform::RotationFlags displayTransformHint) {
- mTransformHintLegacy = getFixedTransformHint();
- if (mTransformHintLegacy == ui::Transform::ROT_INVALID) {
- mTransformHintLegacy = displayTransformHint;
- }
-}
-
-const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
- return mBufferInfo.mBuffer;
-}
-
-bool Layer::setColor(const half3& color) {
- if (mDrawingState.color.rgb == color) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.color.rgb = color;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::fillsColor() const {
- return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf &&
- mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf;
-}
-
-bool Layer::hasBlur() const {
- return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
-}
-
-void Layer::updateSnapshot(bool updateGeometry) {
- if (!getCompositionEngineLayerFE()) {
- return;
- }
-
- auto* snapshot = editLayerSnapshot();
- if (updateGeometry) {
- prepareBasicGeometryCompositionState();
- prepareGeometryCompositionState();
- snapshot->roundedCorner = getRoundedCornerState();
- snapshot->transformedBounds = mScreenBounds;
- if (mEffectiveShadowRadius > 0.f) {
- snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings;
-
- // Note: this preserves existing behavior of shadowing the entire layer and not cropping
- // it if transparent regions are present. This may not be necessary since shadows are
- // typically cast by layers without transparent regions.
- snapshot->shadowSettings.boundaries = mBounds;
-
- const float casterAlpha = snapshot->alpha;
- const bool casterIsOpaque =
- ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState));
-
- // If the casting layer is translucent, we need to fill in the shadow underneath the
- // layer. Otherwise the generated shadow will only be shown around the casting layer.
- snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
- snapshot->shadowSettings.ambientColor *= casterAlpha;
- snapshot->shadowSettings.spotColor *= casterAlpha;
- }
- snapshot->shadowSettings.length = mEffectiveShadowRadius;
- }
- snapshot->contentOpaque = isOpaque(mDrawingState);
- snapshot->layerOpaqueFlagSet =
- (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
- sp<Layer> p = mDrawingParent.promote();
- if (p != nullptr) {
- snapshot->parentTransform = p->getTransform();
- } else {
- snapshot->parentTransform.reset();
- }
- snapshot->bufferSize = getBufferSize(mDrawingState);
- snapshot->externalTexture = mBufferInfo.mBuffer;
- snapshot->hasReadyFrame = hasReadyFrame();
- preparePerFrameCompositionState();
-}
-
-void Layer::updateChildrenSnapshots(bool updateGeometry) {
- for (const sp<Layer>& child : mDrawingChildren) {
- child->updateSnapshot(updateGeometry);
- child->updateChildrenSnapshots(updateGeometry);
- }
-}
-
-void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
- mSnapshot->layerMetadata = parentMetadata;
- mSnapshot->layerMetadata.merge(mDrawingState.metadata);
- for (const sp<Layer>& child : mDrawingChildren) {
- child->updateMetadataSnapshot(mSnapshot->layerMetadata);
- }
-}
-
-void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
- std::unordered_set<Layer*>& visited) {
- if (visited.find(this) != visited.end()) {
- ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName());
- return;
- }
- visited.insert(this);
-
- mSnapshot->relativeLayerMetadata = relativeLayerMetadata;
-
- if (mDrawingState.zOrderRelatives.empty()) {
- return;
- }
- LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata;
- childRelativeLayerMetadata.merge(mSnapshot->layerMetadata);
- for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) {
- sp<Layer> relative = weakRelative.promote();
- if (!relative) {
- continue;
- }
- relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited);
- }
-}
-
bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener) {
bool hadTrustedPresentationListener = hasTrustedPresentationListener();
@@ -4439,39 +1577,41 @@
return haveTrustedPresentationListener;
}
+void Layer::setBufferReleaseChannel(
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) {
+ mBufferReleaseChannel = channel;
+}
+
void Layer::updateLastLatchTime(nsecs_t latchTime) {
mLastLatchTime = latchTime;
}
-void Layer::setIsSmallDirty(const Region& damageRegion,
- const ui::Transform& layerToDisplayTransform) {
- mSmallDirty = false;
+void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) {
+ snapshot->isSmallDirty = false;
return;
}
if (mWindowType != WindowInfo::Type::APPLICATION &&
mWindowType != WindowInfo::Type::BASE_APPLICATION) {
+ snapshot->isSmallDirty = false;
return;
}
- Rect bounds = damageRegion.getBounds();
+ Rect bounds = snapshot->surfaceDamage.getBounds();
if (!bounds.isValid()) {
+ snapshot->isSmallDirty = false;
return;
}
// Transform to screen space.
- bounds = layerToDisplayTransform.transform(bounds);
+ bounds = snapshot->localTransform.transform(bounds);
// If the damage region is a small dirty, this could give the hint for the layer history that
// it could suppress the heuristic rate when calculating.
- mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
- bounds.getWidth() * bounds.getHeight());
-}
-
-void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
- setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform);
- snapshot->isSmallDirty = mSmallDirty;
+ snapshot->isSmallDirty =
+ mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
+ bounds.getWidth() * bounds.getHeight());
}
} // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b9fcd5c..9bc557e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -43,9 +43,7 @@
#include <scheduler/Fps.h>
#include <scheduler/Seamlessness.h>
-#include <chrono>
#include <cstdint>
-#include <list>
#include <optional>
#include <vector>
@@ -56,7 +54,6 @@
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
#include "SurfaceFlinger.h"
-#include "Tracing/LayerTracing.h"
#include "TransactionCallbackInvoker.h"
using namespace android::surfaceflinger;
@@ -91,48 +88,15 @@
// Windows that are not in focus, but voted for a specific mode ID.
static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
- enum { // flags for doTransaction()
- eDontUpdateGeometryState = 0x00000001,
- eVisibleRegion = 0x00000002,
- eInputInfoChanged = 0x00000004
- };
-
- struct Geometry {
- uint32_t w;
- uint32_t h;
- ui::Transform transform;
-
- inline bool operator==(const Geometry& rhs) const {
- return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) &&
- (transform.ty() == rhs.transform.ty());
- }
- inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
- };
-
using FrameRate = scheduler::LayerInfo::FrameRate;
using FrameRateCompatibility = scheduler::FrameRateCompatibility;
using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
struct State {
- int32_t z;
- ui::LayerStack layerStack;
- uint32_t flags;
int32_t sequence; // changes when visible regions can change
- bool modified;
// Crop is expressed in layer space coordinate.
Rect crop;
LayerMetadata metadata;
- // If non-null, a Surface this Surface's Z-order is interpreted relative to.
- wp<Layer> zOrderRelativeOf;
- bool isRelativeOf{false};
-
- // A list of surfaces whose Z-order is interpreted relative to ours.
- SortedVector<wp<Layer>> zOrderRelatives;
- half4 color;
- float cornerRadius;
- int backgroundBlurRadius;
- gui::WindowInfo inputInfo;
- wp<Layer> touchableRegionCrop;
ui::Dataspace dataspace;
@@ -154,52 +118,18 @@
std::shared_ptr<renderengine::ExternalTexture> buffer;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
- HdrMetadata hdrMetadata;
- Region surfaceDamageRegion;
- int32_t api;
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
- bool hasColorTransform;
- // pointer to background color layer that, if set, appears below the buffer state layer
- // and the buffer state layer's children. Z order will be set to
- // INT_MIN
- sp<Layer> bgColorLayer;
// The deque of callback handles for this frame. The back of the deque contains the most
// recent callback handle.
std::deque<sp<CallbackHandle>> callbackHandles;
- bool colorSpaceAgnostic;
nsecs_t desiredPresentTime = 0;
bool isAutoTimestamp = true;
- // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
- // be rendered around the layer.
- float shadowRadius;
-
- // Layer regions that are made of custom materials, like frosted glass
- std::vector<BlurRegion> blurRegions;
-
- // Priority of the layer assigned by Window Manager.
- int32_t frameRateSelectionPriority;
-
- // Default frame rate compatibility used to set the layer refresh rate votetype.
- FrameRateCompatibility defaultFrameRateCompatibility;
- FrameRate frameRate;
-
// The combined frame rate of parents / children of this layer
FrameRate frameRateForLayerTree;
- FrameRateSelectionStrategy frameRateSelectionStrategy;
-
- // Set by window manager indicating the layer and all its children are
- // in a different orientation than the display. The hint suggests that
- // the graphic producers should receive a transform hint as if the
- // display was in this orientation. When the display changes to match
- // the layer orientation, the graphic producer may not need to allocate
- // a buffer of a different size. ui::Transform::ROT_INVALID means the
- // a fixed transform hint is not set.
- ui::Transform::RotationFlags fixedTransformHint;
-
// The vsync info that was used to start the transaction
FrameTimelineInfo frameTimelineInfo;
@@ -219,21 +149,12 @@
// An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to
// trigger a warning if the number of SurfaceFrames crosses the threshold.
static constexpr uint32_t kStateSurfaceFramesThreshold = 25;
-
- // Stretch effect to apply to this layer
- StretchEffect stretchEffect;
-
- // Whether or not this layer is a trusted overlay for input
- bool isTrustedOverlay;
Rect bufferCrop;
Rect destinationFrame;
sp<IBinder> releaseBufferEndpoint;
- gui::DropInputMode dropInputMode;
bool autoRefresh = false;
- bool dimmingEnabled = true;
float currentHdrSdrRatio = 1.f;
float desiredHdrSdrRatio = -1.f;
- gui::CachingHint cachingHint = gui::CachingHint::Enabled;
int64_t latchedVsyncId = 0;
bool useVsyncIdForRefreshRateSelection = false;
};
@@ -244,202 +165,49 @@
static bool isLayerFocusedBasedOnPriority(int32_t priority);
static void miniDumpHeader(std::string& result);
- // Provide unique string for each class type in the Layer hierarchy
- virtual const char* getType() const { return "Layer"; }
-
- // true if this layer is visible, false otherwise
- virtual bool isVisible() const;
-
- virtual sp<Layer> createClone();
-
- // Set a 2x2 transformation matrix on the layer. This transform
- // will be applied after parent transforms, but before any final
- // producer specified transform.
- bool setMatrix(const layer_state_t::matrix22_t& matrix);
-
// This second set of geometry attributes are controlled by
// setGeometryAppliesWithResize, and their default mode is to be
// immediate. If setGeometryAppliesWithResize is specified
// while a resize is pending, then update of these attributes will
// be delayed until the resize completes.
- // setPosition operates in parent buffer space (pre parent-transform) or display
- // space for top-level layers.
- bool setPosition(float x, float y);
// Buffer space
bool setCrop(const Rect& crop);
- // TODO(b/38182121): Could we eliminate the various latching modes by
- // using the layer hierarchy?
- // -----------------------------------------------------------------------
- virtual bool setLayer(int32_t z);
- virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
-
- virtual bool setAlpha(float alpha);
- bool setColor(const half3& /*color*/);
-
- // Set rounded corner radius for this layer and its children.
- //
- // We only support 1 radius per layer in the hierarchy, where parent layers have precedence.
- // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
- // from which we inferred the rounded corner radius.
- virtual bool setCornerRadius(float cornerRadius);
- // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
- // is specified in pixels.
- virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
- virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
- bool setTransparentRegionHint(const Region& transparent);
- virtual bool setTrustedOverlay(bool);
- virtual bool setFlags(uint32_t flags, uint32_t mask);
- virtual bool setLayerStack(ui::LayerStack);
- virtual ui::LayerStack getLayerStack(
- LayerVector::StateSet state = LayerVector::StateSet::Drawing) const;
-
- virtual bool setMetadata(const LayerMetadata& data);
- virtual void setChildrenDrawingParent(const sp<Layer>&);
- virtual bool reparent(const sp<IBinder>& newParentHandle) REQUIRES(mFlinger->mStateLock);
- virtual bool setColorTransform(const mat4& matrix);
- virtual mat4 getColorTransform() const;
- virtual bool hasColorTransform() const;
- virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
- virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
- float getDesiredHdrSdrRatio() const { return getDrawingState().desiredHdrSdrRatio; }
- float getCurrentHdrSdrRatio() const { return getDrawingState().currentHdrSdrRatio; }
- gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; }
-
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const FrameTimelineInfo& /*info*/);
+ const FrameTimelineInfo& /*info*/, gui::GameMode gameMode);
void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
bool setDesiredHdrHeadroom(float desiredRatio);
- bool setCachingHint(gui::CachingHint cachingHint);
- bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
- bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
- bool setApi(int32_t /*api*/);
bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/,
- const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */);
+ const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */,
+ gui::GameMode gameMode);
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/,
bool willPresent);
- virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace)
- REQUIRES(mFlinger->mStateLock);
- virtual bool setColorSpaceAgnostic(const bool agnostic);
- virtual bool setDimmingEnabled(const bool dimmingEnabled);
- virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility);
- virtual bool setFrameRateSelectionPriority(int32_t priority);
- virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
- void setAutoRefresh(bool /* autoRefresh */);
- bool setDropInputMode(gui::DropInputMode);
- // If the variable is not set on the layer, it traverses up the tree to inherit the frame
- // rate priority from its parent.
- virtual int32_t getFrameRateSelectionPriority() const;
- //
- virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const;
- //
- ui::Dataspace getDataSpace() const;
-
- virtual bool isFrontBuffered() const;
-
- virtual sp<LayerFE> getCompositionEngineLayerFE() const;
- virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
- sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
-
- const frontend::LayerSnapshot* getLayerSnapshot() const;
- frontend::LayerSnapshot* editLayerSnapshot();
- std::unique_ptr<frontend::LayerSnapshot> stealLayerSnapshot();
- void updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot);
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
// one empty rect.
- void useSurfaceDamage();
- void useEmptyDamage();
Region getVisibleRegion(const DisplayDevice*) const;
void updateLastLatchTime(nsecs_t latchtime);
- /*
- * isOpaque - true if this surface is opaque
- *
- * This takes into account the buffer format (i.e. whether or not the
- * pixel format includes an alpha channel) and the "opaque" flag set
- * on the layer. It does not examine the current plane alpha value.
- */
- bool isOpaque(const Layer::State&) const;
-
- /*
- * Returns whether this layer can receive input.
- */
- bool canReceiveInput() const;
-
- /*
- * Whether or not the layer should be considered visible for input calculations.
- */
- virtual bool isVisibleForInput() const {
- // For compatibility reasons we let layers which can receive input
- // receive input before they have actually submitted a buffer. Because
- // of this we use canReceiveInput instead of isVisible to check the
- // policy-visibility, ignoring the buffer state. However for layers with
- // hasInputInfo()==false we can use the real visibility state.
- // We are just using these layers for occlusion detection in
- // InputDispatcher, and obviously if they aren't visible they can't occlude
- // anything.
- return hasInputInfo() ? canReceiveInput() : isVisible();
- }
-
- /*
- * isProtected - true if the layer may contain protected contents in the
- * GRALLOC_USAGE_PROTECTED sense.
- */
- bool isProtected() const;
-
- /*
- * isFixedSize - true if content has a fixed size
- */
- virtual bool isFixedSize() const { return true; }
-
- /*
- * usesSourceCrop - true if content should use a source crop
- */
- bool usesSourceCrop() const { return hasBufferOrSidebandStream(); }
-
- // Most layers aren't created from the main thread, and therefore need to
- // grab the SF state lock to access HWC, but ContainerLayer does, so we need
- // to avoid grabbing the lock again to avoid deadlock
- virtual bool isCreatedFromMainThread() const { return false; }
-
- ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
- Region getActiveTransparentRegion(const Layer::State& s) const {
- return s.transparentRegionHint;
- }
Rect getCrop(const Layer::State& s) const { return s.crop; }
- bool needsFiltering(const DisplayDevice*) const;
-
- // True if this layer requires filtering
- // This method is distinct from needsFiltering() in how the filter
- // requirement is computed. needsFiltering() compares displayFrame and crop,
- // where as this method transforms the displayFrame to layer-stack space
- // first. This method should be used if there is no physical display to
- // project onto when taking screenshots, as the filtering requirements are
- // different.
- // If the parent transform needs to be undone when capturing the layer, then
- // the inverse parent transform is also required.
- bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
// from graphics API
static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
- void updateCloneBufferInfo();
uint64_t mPreviousFrameNumber = 0;
void onCompositionPresented(const DisplayDevice*,
const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming&);
+ const CompositorTiming&, gui::GameMode gameMode);
// If a buffer was replaced this frame, release the former buffer
void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
@@ -450,46 +218,10 @@
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/);
-
bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
bool bgColorOnly);
- /*
- * Returns true if the currently presented buffer will be released when this layer state
- * is latched. This will return false if there is no buffer currently presented.
- */
- bool willReleaseBufferOnLatch() const;
-
- /*
- * Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
- * This is used if the buffer is just latched and releases to free up the buffer
- * and will not be shown on screen.
- * Should only be called on the main thread.
- */
- void latchAndReleaseBuffer();
-
- /*
- * returns the rectangle that crops the content of the layer and scales it
- * to the layer's size.
- */
- Rect getBufferCrop() const;
-
- /*
- * Returns the transform applied to the buffer.
- */
- uint32_t getBufferTransform() const;
-
sp<GraphicBuffer> getBuffer() const;
- const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const;
-
- /*
- * Returns if a frame is ready
- */
- bool hasReadyFrame() const;
-
- virtual int32_t getQueuedFrameCount() const { return 0; }
-
/**
* Returns active buffer size in the correct orientation. Buffer size is determined by undoing
* any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the
@@ -497,33 +229,10 @@
*/
Rect getBufferSize(const Layer::State&) const;
- /**
- * Returns the source bounds. If the bounds are not defined, it is inferred from the
- * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
- * For the root layer, this is the display viewport size.
- */
- FloatRect computeSourceBounds(const FloatRect& parentBounds) const;
- virtual FrameRate getFrameRateForLayerTree() const;
+ FrameRate getFrameRateForLayerTree() const;
bool getTransformToDisplayInverse() const;
- // Returns how rounded corners should be drawn for this layer.
- // A layer can override its parent's rounded corner settings if the parent's rounded
- // corner crop does not intersect with its own rounded corner crop.
- virtual frontend::RoundedCornerState getRoundedCornerState() const;
-
- bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); }
-
- PixelFormat getPixelFormat() const;
- /**
- * Return whether this layer needs an input info. We generate InputWindowHandles for all
- * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable
- * the InputDispatcher to do PID based occlusion detection.
- */
- bool needsInputInfo() const {
- return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor;
- }
-
// Implements RefBase.
void onFirstRef() override;
@@ -534,25 +243,18 @@
uint32_t mTransform{0};
ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
Rect mCrop;
- uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
- Region mSurfaceDamage;
- HdrMetadata mHdrMetadata;
- int mApi;
PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
bool mTransformToDisplayInverse{false};
-
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
sp<IBinder> mReleaseBufferEndpoint;
-
bool mFrameLatencyNeeded{false};
float mDesiredHdrSdrRatio = -1.f;
};
BufferInfo mBufferInfo;
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseChannel;
- // implements compositionengine::LayerFE
- const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
void onPreComposition(nsecs_t refreshStartTime);
void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
@@ -573,17 +275,6 @@
const char* getDebugName() const;
- bool setShadowRadius(float shadowRadius);
-
- // Before color management is introduced, contents on Android have to be
- // desaturated in order to match what they appears like visually.
- // With color management, these contents will appear desaturated, thus
- // needed to be saturated so that they match what they are designed for
- // visually.
- bool isLegacyDataSpace() const;
-
- uint32_t getTransactionFlags() const { return mTransactionFlags; }
-
static bool computeTrustedPresentationState(const FloatRect& bounds,
const FloatRect& sourceBounds,
const Region& coveredRegion,
@@ -601,17 +292,6 @@
// Sets the masked bits.
void setTransactionFlags(uint32_t mask);
- // Clears and returns the masked bits.
- uint32_t clearTransactionFlags(uint32_t mask);
-
- FloatRect getBounds(const Region& activeTransparentRegion) const;
- FloatRect getBounds() const;
- Rect getInputBoundsInDisplaySpace(const FloatRect& insetBounds,
- const ui::Transform& displayTransform);
-
- // Compute bounds for the layer and cache the results.
- void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
-
int32_t getSequence() const { return sequence; }
// For tracing.
@@ -622,164 +302,21 @@
// only used within a single layer.
uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
- /*
- * isSecure - true if this surface is secure, that is if it prevents
- * screenshots or VNC servers. A surface can be set to be secure by the
- * application, being secure doesn't mean the surface has DRM contents.
- */
- bool isSecure() const;
-
- /*
- * isHiddenByPolicy - true if this layer has been forced invisible.
- * just because this is false, doesn't mean isVisible() is true.
- * For example if this layer has no active buffer, it may not be hidden by
- * policy, but it still can not be visible.
- */
- bool isHiddenByPolicy() const;
-
- // True if the layer should be skipped in screenshots, screen recordings,
- // and mirroring to external or virtual displays.
- bool isInternalDisplayOverlay() const;
-
- ui::LayerFilter getOutputFilter() const {
- return {getLayerStack(), isInternalDisplayOverlay()};
- }
-
- bool isRemovedFromCurrentState() const;
-
- perfetto::protos::LayerProto* writeToProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags);
void writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto,
ui::LayerStack layerStack);
- // Write states that are modified by the main thread. This includes drawing
- // state as well as buffer data. This should be called in the main or tracing
- // thread.
- void writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo);
- // Write drawing or current state. If writing current state, the caller should hold the
- // external mStateLock. If writing drawing state, this function should be called on the
- // main or tracing thread.
- void writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo, LayerVector::StateSet,
- uint32_t traceFlags = LayerTracing::TRACE_ALL);
-
- gui::WindowInfo::Type getWindowType() const { return mWindowType; }
-
- bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates);
-
- /*
- * doTransaction - process the transaction. This is a good place to figure
- * out which attributes of the surface have changed.
- */
- virtual uint32_t doTransaction(uint32_t transactionFlags);
-
- /*
- * Remove relative z for the layer if its relative parent is not part of the
- * provided layer tree.
- */
- void removeRelativeZ(const std::vector<Layer*>& layersInTree);
-
- /*
- * Remove from current state and mark for removal.
- */
- void removeFromCurrentState() REQUIRES(mFlinger->mStateLock);
-
- /*
- * called with the state lock from a binder thread when the layer is
- * removed from the current list to the pending removal list
- */
- void onRemovedFromCurrentState() REQUIRES(mFlinger->mStateLock);
-
- /*
- * Called when the layer is added back to the current state list.
- */
- void addToCurrentState();
-
- /*
- * Sets display transform hint on BufferLayerConsumer.
- */
- void updateTransformHint(ui::Transform::RotationFlags);
inline const State& getDrawingState() const { return mDrawingState; }
inline State& getDrawingState() { return mDrawingState; }
- void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
- void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
void onDisconnect();
- ui::Transform getTransform() const;
- bool isTransformValid() const;
-
- // Returns the Alpha of the Surface, accounting for the Alpha
- // of parent Surfaces in the hierarchy (alpha's will be multiplied
- // down the hierarchy).
- half getAlpha() const;
- half4 getColor() const;
- int32_t getBackgroundBlurRadius() const;
- bool drawShadows() const { return mEffectiveShadowRadius > 0.f; };
-
- // Returns the transform hint set by Window Manager on the layer or one of its parents.
- // This traverses the current state because the data is needed when creating
- // the layer(off drawing thread) and the hint should be available before the producer
- // is ready to acquire a buffer.
- ui::Transform::RotationFlags getFixedTransformHint() const;
-
- /**
- * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
- * which will not emit children who have relativeZOrder to another layer, this method
- * just directly emits all children. It also emits them in no particular order.
- * So this method is not suitable for graphical operations, as it doesn't represent
- * the scene state, but it's also more efficient than traverseInZOrder and so useful for
- * book-keeping.
- */
- void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
- void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
- void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
- void traverseChildren(const LayerVector::Visitor&);
-
- /**
- * Traverse only children in z order, ignoring relative layers that are not children of the
- * parent.
- */
- void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
-
- size_t getDescendantCount() const;
- size_t getChildrenCount() const { return mDrawingChildren.size(); }
- bool isHandleAlive() const { return mHandleAlive; }
bool onHandleDestroyed() { return mHandleAlive = false; }
- // ONLY CALL THIS FROM THE LAYER DTOR!
- // See b/141111965. We need to add current children to offscreen layers in
- // the layer dtor so as not to dangle layers. Since the layer has not
- // committed its transaction when the layer is destroyed, we must add
- // current children. This is safe in the dtor as we will no longer update
- // the current state, but should not be called anywhere else!
- LayerVector& getCurrentChildren() { return mCurrentChildren; }
-
- void addChild(const sp<Layer>&);
- // Returns index if removed, or negative value otherwise
- // for symmetry with Vector::remove
- ssize_t removeChild(const sp<Layer>& layer);
- sp<Layer> getParent() const { return mCurrentParent.promote(); }
-
- // Should be called with the surfaceflinger statelock held
- bool isAtRoot() const { return mIsAtRoot; }
- void setIsAtRoot(bool isAtRoot) { mIsAtRoot = isAtRoot; }
-
- bool hasParent() const { return getParent() != nullptr; }
- Rect getScreenBounds(bool reduceTransparentRegion = true) const;
- bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
- bool setChildRelativeLayer(const sp<Layer>& childLayer,
- const sp<IBinder>& relativeToHandle, int32_t relativeZ);
-
- // Copy the current list of children to the drawing state. Called by
- // SurfaceFlinger to complete a transaction.
- void commitChildList();
- int32_t getZ(LayerVector::StateSet) const;
-
/**
* Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
* INVALID_RECT if the layer has no buffer and no crop.
@@ -788,15 +325,10 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- bool setFrameRate(FrameRate::FrameRateVote);
- bool setFrameRateCategory(FrameRateCategory, bool smoothSwitchOnly);
-
- bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy);
-
- virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
- void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime);
+ void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime,
+ gui::GameMode gameMode);
void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime);
+ nsecs_t postTime, gui::GameMode gameMode);
void addSurfaceFrameDroppedForBuffer(std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame,
nsecs_t dropTime);
@@ -805,60 +337,25 @@
nsecs_t currentLatchTime);
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForTransaction(
- const FrameTimelineInfo& info, nsecs_t postTime);
+ const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode);
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
- const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
+ const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
+ gui::GameMode gameMode);
void setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
- std::string debugName);
+ std::string debugName, gui::GameMode gameMode);
bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener);
+ void setBufferReleaseChannel(
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
// Creates a new handle each time, so we only expect
// this to be called once.
sp<IBinder> getHandle();
const std::string& getName() const { return mName; }
- bool getPremultipledAlpha() const;
- void setInputInfo(const gui::WindowInfo& info);
-
- struct InputDisplayArgs {
- const ui::Transform* transform = nullptr;
- bool isSecure = false;
- };
- gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs);
-
- /**
- * Returns whether this layer has an explicitly set input-info.
- */
- bool hasInputInfo() const;
-
- // Sets the gui::GameMode for the tree rooted at this layer. A layer in the tree inherits this
- // gui::GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
- void setGameModeForTree(gui::GameMode);
-
- void setGameMode(gui::GameMode gameMode) { mGameMode = gameMode; }
- gui::GameMode getGameMode() const { return mGameMode; }
virtual uid_t getOwnerUid() const { return mOwnerUid; }
- pid_t getOwnerPid() { return mOwnerPid; }
-
- int32_t getOwnerAppId() { return mOwnerAppId; }
-
- // This layer is not a clone, but it's the parent to the cloned hierarchy. The
- // variable mClonedChild represents the top layer that will be cloned so this
- // layer will be the parent of mClonedChild.
- // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
- // if the real layer is destroyed, then the clone layer will also be destroyed.
- sp<Layer> mClonedChild;
- bool mHadClonedChild = false;
- void setClonedChild(const sp<Layer>& mClonedChild);
-
- mutable bool contentDirty{false};
- Region surfaceDamageRegion;
-
- // True when the surfaceDamageRegion is recognized as a small area update.
- bool mSmallDirty{false};
// Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating.
nsecs_t mMaxTimeForUseVsyncId = 0;
// True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating
@@ -870,61 +367,16 @@
// the same.
const int32_t sequence;
- bool mPendingHWCDestroy{false};
-
- bool backpressureEnabled() const {
- return mDrawingState.flags & layer_state_t::eEnableBackpressure;
- }
-
- bool setStretchEffect(const StretchEffect& effect);
- StretchEffect getStretchEffect() const;
-
- bool setBufferCrop(const Rect& /* bufferCrop */);
- bool setDestinationFrame(const Rect& /* destinationFrame */);
// See mPendingBufferTransactions
void decrementPendingBufferCount();
std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
std::string getPendingBufferCounterName() { return mBlastTransactionName; }
- bool updateGeometry();
-
- bool isSimpleBufferUpdate(const layer_state_t& s) const;
-
- static bool isOpaqueFormat(PixelFormat format);
-
- // Updates the LayerSnapshot. This must be called prior to sending layer data to
- // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
- // LayerFE::prepareClientComposition).
- //
- // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
- // CompositionEngine to create a single path for composing layers.
- void updateSnapshot(bool updateGeometry);
- void updateChildrenSnapshots(bool updateGeometry);
- void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
- void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
- std::unordered_set<Layer*>& visited);
- sp<Layer> getClonedFrom() const {
- return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr;
- }
- bool isClone() { return mClonedFrom != nullptr; }
-
- bool willPresentCurrentTransaction() const;
-
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
- bool setFrameRateForLayerTreeLegacy(FrameRate, nsecs_t now);
bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&, nsecs_t now);
void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
- auto getLayerProps() const {
- return scheduler::LayerProps{.visible = isVisible(),
- .bounds = getBounds(),
- .transform = getTransform(),
- .setFrameRateVote = getFrameRateForLayerTree(),
- .frameRateSelectionPriority = getFrameRateSelectionPriority(),
- .isSmallDirty = mSmallDirty,
- .isFrontBuffered = isFrontBuffered()};
- };
bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
mTransformHint = transformHint;
@@ -964,7 +416,6 @@
const sp<SurfaceFlinger> mFlinger;
// Check if the damage region is a small dirty.
- void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform);
void setIsSmallDirty(frontend::LayerSnapshot* snapshot);
protected:
@@ -976,63 +427,16 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- void preparePerFrameCompositionState();
- void preparePerFrameBufferCompositionState();
- void preparePerFrameEffectsCompositionState();
void gatherBufferInfo();
- void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
- bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
-
- void cloneDrawingState(const Layer* from);
- void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedChildren(const sp<Layer>& mirrorRoot,
- std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void addChildToDrawing(const sp<Layer>&);
- void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
-
- void prepareBasicGeometryCompositionState();
- void prepareGeometryCompositionState();
- void prepareCursorCompositionState();
-
- uint32_t getEffectiveUsage(uint32_t usage) const;
-
- /**
- * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
- * crop coordinates, transforming them into layer space.
- */
- void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
- void setParent(const sp<Layer>&);
- LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
- void addZOrderRelative(const wp<Layer>& relative);
- void removeZOrderRelative(const wp<Layer>& relative);
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
compositionengine::OutputLayer* findOutputLayerForDisplay(
const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const;
- bool usingRelativeZ(LayerVector::StateSet) const;
- virtual ui::Transform getInputTransform() const;
- /**
- * Get the bounds in layer space within which this layer can receive input.
- *
- * These bounds are used to:
- * - Determine the input frame for the layer to be used for occlusion detection; and
- * - Determine the coordinate space within which the layer will receive input. The top-left of
- * this rect will be the origin of the coordinate space that the input events sent to the
- * layer will be in (prior to accounting for surface insets).
- *
- * The layer can still receive touch input if these bounds are invalid if
- * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input
- * in this layer's space, regardless of the specified crop layer.
- */
- std::pair<FloatRect, bool> getInputBounds(bool fillParentBounds) const;
-
- bool mPremultipliedAlpha{true};
const std::string mName;
const std::string mTransactionName{"TX - " + mName};
- // These are only accessed by the main thread or the tracing thread.
+ // These are only accessed by the main thread.
State mDrawingState;
TrustedPresentationThresholds mTrustedPresentationThresholds;
@@ -1042,44 +446,22 @@
int64_t mEnteredTrustedPresentationStateTime = -1;
uint32_t mTransactionFlags{0};
- // Updated in doTransaction, used to track the last sequence number we
- // committed. Currently this is really only used for updating visible
- // regions.
- int32_t mLastCommittedTxSequence = -1;
// Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
// main thread
sp<NativeHandle> mSidebandStream;
- // False if the buffer and its contents have been previously used for GPU
- // composition, true otherwise.
- bool mIsActiveBufferUpdatedForGpu = true;
// We encode unset as -1.
std::atomic<uint64_t> mCurrentFrameNumber{0};
- // Whether filtering is needed b/c of the drawingstate
- bool mNeedsFiltering{false};
-
- std::atomic<bool> mRemovedFromDrawingState{false};
-
- // page-flip thread (currently main thread)
- bool mProtectedByApp{false}; // application requires protected path to external sink
// protected by mLock
mutable Mutex mLock;
- const wp<Client> mClientRef;
-
// This layer can be a cursor on some displays.
bool mPotentialCursor{false};
- LayerVector mCurrentChildren{LayerVector::StateSet::Current};
- LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
-
- wp<Layer> mCurrentParent;
- wp<Layer> mDrawingParent;
-
// Window types from WindowManager.LayoutParams
const gui::WindowInfo::Type mWindowType;
@@ -1097,8 +479,6 @@
// Used in buffer stuffing analysis in FrameTimeline.
nsecs_t mLastLatchTime = 0;
- mutable bool mDrawingStateModified = false;
-
sp<Fence> mLastClientCompositionFence;
bool mClearClientCompositionFenceOnLayerDisplayed = false;
private:
@@ -1110,62 +490,20 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
std::atomic<bool> mSidebandStreamChanged{false};
- // Returns true if the layer can draw shadows on its border.
- virtual bool canDrawShadows() const { return true; }
-
aidl::android::hardware::graphics::composer3::Composition getCompositionType(
const DisplayDevice&) const;
aidl::android::hardware::graphics::composer3::Composition getCompositionType(
const compositionengine::OutputLayer*) const;
- /**
- * Returns an unsorted vector of all layers that are part of this tree.
- * That includes the current layer and all its descendants.
- */
- std::vector<Layer*> getLayersInTree(LayerVector::StateSet);
- /**
- * Traverses layers that are part of this tree in the correct z order.
- * layersInTree must be sorted before calling this method.
- */
- void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree,
- LayerVector::StateSet, const LayerVector::Visitor&);
- LayerVector makeChildrenTraversalList(LayerVector::StateSet,
- const std::vector<Layer*>& layersInTree);
-
- void updateTreeHasFrameRateVote();
- bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
- bool* transactionNeeded);
- void setZOrderRelativeOf(const wp<Layer>& relativeOf);
- bool isTrustedOverlay() const;
- gui::DropInputMode getDropInputMode() const;
- void handleDropInputMode(gui::WindowInfo& info) const;
-
- // Find the root of the cloned hierarchy, this means the first non cloned parent.
- // This will return null if first non cloned parent is not found.
- sp<Layer> getClonedRoot();
-
- // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
- // null.
- sp<Layer> getRootLayer();
-
- // Fills in the touch occlusion mode of the first parent (including this layer) that
- // hasInputInfo() or no-op if no such parent is found.
- void fillTouchOcclusionMode(gui::WindowInfo& info);
-
- // Fills in the frame and transform info for the gui::WindowInfo.
- void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
inline void tracePendingBufferCount(int32_t pendingBuffers);
// Latch sideband stream and returns true if the dirty region should be updated.
bool latchSidebandStream(bool& recomputeVisibleRegions);
- bool hasFrameUpdate() const;
-
void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false);
// Crop that applies to the buffer
@@ -1176,15 +514,6 @@
const sp<Fence>& releaseFence,
uint32_t currentMaxAcquiredBufferCount);
- // Returns true if the transformed buffer size does not match the layer size and we need
- // to apply filtering.
- bool bufferNeedsFiltering() const;
-
- // Returns true if there is a valid color to fill.
- bool fillsColor() const;
- // Returns true if this layer has a blur value.
- bool hasBlur() const;
- bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
bool hasBufferOrSidebandStream() const {
return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr));
}
@@ -1193,46 +522,8 @@
return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr));
}
- bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
-
- // Fills the provided vector with the currently available JankData and removes the processed
- // JankData from the pending list.
- void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
- std::vector<JankData>& jankData);
-
- bool shouldOverrideChildrenFrameRate() const {
- return getDrawingState().frameRateSelectionStrategy ==
- FrameRateSelectionStrategy::OverrideChildren;
- }
-
- bool shouldPropagateFrameRate() const {
- return getDrawingState().frameRateSelectionStrategy != FrameRateSelectionStrategy::Self;
- }
-
- // Cached properties computed from drawing state
- // Effective transform taking into account parent transforms and any parent scaling, which is
- // a transform from the current layer coordinate space to display(screen) coordinate space.
- ui::Transform mEffectiveTransform;
-
- // Bounds of the layer before any transformation is applied and before it has been cropped
- // by its parents.
- FloatRect mSourceBounds;
-
- // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and
- // its parent bounds.
- FloatRect mBounds;
-
- // Layer bounds in screen space.
- FloatRect mScreenBounds;
-
bool mGetHandleCalled = false;
- // The current layer is a clone of mClonedFrom. This means that this layer will update it's
- // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
- // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
- // and relatives, this layer will update as well.
- wp<Layer> mClonedFrom;
-
// The inherited shadow radius after taking into account the layer hierarchy. This is the
// final shadow radius for this layer. If a shadow is specified for a layer, then effective
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
@@ -1241,22 +532,15 @@
// Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
gui::GameMode mGameMode = gui::GameMode::Unsupported;
- // A list of regions on this layer that should have blurs.
- const std::vector<BlurRegion> getBlurRegions() const;
-
bool mIsAtRoot = false;
uint32_t mLayerCreationFlags;
- bool findInHierarchy(const sp<Layer>&);
-
- void setTransformHintLegacy(ui::Transform::RotationFlags);
void releasePreviousBuffer();
void resetDrawingStateBufferInfo();
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
- ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
@@ -1268,30 +552,23 @@
// time.
std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1;
- std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
- // An upper bound on the number of SurfaceFrames in the pending classifications deque.
- static constexpr int kPendingClassificationMaxSurfaceFrames = 50;
-
const std::string mBlastTransactionName{"BufferTX - " + mName};
// This integer is incremented everytime a buffer arrives at the server for this layer,
// and decremented when a buffer is dropped or latched. When changed the integer is exported
- // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
+ // to systrace with SFTRACE_INT and mBlastTransactionName. This way when debugging perf it is
// possible to see when a buffer arrived at the server, and in which frame it latched.
//
// You can understand the trace this way:
// - If the integer increases, a buffer arrived at the server.
// - If the integer decreases in latchBuffer, that buffer was latched
- // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
+ // - If the integer decreases in setBuffer, a buffer was dropped
std::atomic<int32_t> mPendingBufferTransactions{0};
// Contains requested position and matrix updates. This will be applied if the client does
// not specify a destination frame.
ui::Transform mRequestedTransform;
- sp<LayerFE> mLegacyLayerFE;
std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
- std::unique_ptr<frontend::LayerSnapshot> mSnapshot =
- std::make_unique<frontend::LayerSnapshot>();
bool mHandleAlive = false;
};
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index c2251a8..b05f0ee 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -19,11 +19,10 @@
#define LOG_TAG "SurfaceFlinger"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <common/trace.h>
#include <gui/GLConsumer.h>
-#include <gui/TraceUtils.h>
#include <math/vec3.h>
#include <system/window.h>
-#include <utils/Log.h>
#include "LayerFE.h"
#include "SurfaceFlinger.h"
@@ -122,7 +121,7 @@
std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal(
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- ATRACE_CALL();
+ SFTRACE_CALL();
compositionengine::LayerFE::LayerSettings layerSettings;
layerSettings.geometry.boundaries =
reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint);
@@ -174,6 +173,7 @@
break;
}
layerSettings.stretchEffect = mSnapshot->stretchEffect;
+ layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect;
// Record the name of the layer for debugging further down the stack.
layerSettings.name = mSnapshot->name;
@@ -214,7 +214,7 @@
void LayerFE::prepareBufferStateClientComposition(
compositionengine::LayerFE::LayerSettings& layerSettings,
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (CC_UNLIKELY(!mSnapshot->externalTexture)) {
// If there is no buffer for the layer or we have sidebandstream where there is no
// activeBuffer, then we need to return LayerSettings.
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 496033b..5eea45b 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -178,7 +178,7 @@
}
void LayerProtoHelper::writeToProto(
- const WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
+ const WindowInfo& inputInfo,
std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto) {
if (inputInfo.token == nullptr) {
return;
@@ -208,13 +208,6 @@
proto->set_global_scale_factor(inputInfo.globalScaleFactor);
LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform());
proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
- auto cropLayer = touchableRegionBounds.promote();
- if (cropLayer != nullptr) {
- proto->set_crop_layer_id(cropLayer->sequence);
- LayerProtoHelper::writeToProto(cropLayer->getScreenBounds(
- false /* reduceTransparentRegion */),
- [&]() { return proto->mutable_touchable_region_crop(); });
- }
}
void LayerProtoHelper::writeToProto(const mat4 matrix,
@@ -263,9 +256,10 @@
outRegion.bottom = proto.bottom();
}
-perfetto::protos::LayersProto LayerProtoFromSnapshotGenerator::generate(
+LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::with(
const frontend::LayerHierarchy& root) {
mLayersProto.clear_layers();
+ mVisitedLayers.clear();
std::unordered_set<uint64_t> stackIdsToSkip;
if ((mTraceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
for (const auto& [layerStack, displayInfo] : mDisplayInfos) {
@@ -304,9 +298,40 @@
}
}
- mDefaultSnapshots.clear();
- mChildToRelativeParent.clear();
- return std::move(mLayersProto);
+ return *this;
+}
+
+LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::withOffscreenLayers(
+ const frontend::LayerHierarchy& offscreenRoot) {
+ // Add a fake invisible root layer to the proto output and parent all the offscreen layers to
+ // it.
+ perfetto::protos::LayerProto* rootProto = mLayersProto.add_layers();
+ const int32_t offscreenRootLayerId = INT32_MAX - 2;
+ rootProto->set_id(offscreenRootLayerId);
+ rootProto->set_name("Offscreen Root");
+ rootProto->set_parent(-1);
+
+ perfetto::protos::LayersProto offscreenLayers =
+ LayerProtoFromSnapshotGenerator(mSnapshotBuilder, mDisplayInfos, mLegacyLayers,
+ mTraceFlags)
+ .with(offscreenRoot)
+ .generate();
+
+ for (int i = 0; i < offscreenLayers.layers_size(); i++) {
+ perfetto::protos::LayerProto* layerProto = offscreenLayers.mutable_layers()->Mutable(i);
+ if (layerProto->parent() == -1) {
+ layerProto->set_parent(offscreenRootLayerId);
+ // Add layer as child of the fake root
+ rootProto->add_children(layerProto->id());
+ }
+ }
+
+ mLayersProto.mutable_layers()->Reserve(mLayersProto.layers_size() +
+ offscreenLayers.layers_size());
+ std::copy(offscreenLayers.layers().begin(), offscreenLayers.layers().end(),
+ RepeatedFieldBackInserter(mLayersProto.mutable_layers()));
+
+ return *this;
}
frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot(
@@ -326,6 +351,11 @@
perfetto::protos::LayerProto* layerProto = mLayersProto.add_layers();
const frontend::RequestedLayerState& layer = *root.getLayer();
frontend::LayerSnapshot* snapshot = getSnapshot(path, layer);
+ if (mVisitedLayers.find(snapshot->uniqueSequence) != mVisitedLayers.end()) {
+ TransactionTraceWriter::getInstance().invoke("DuplicateLayer", /* overwrite= */ false);
+ return;
+ }
+ mVisitedLayers.insert(snapshot->uniqueSequence);
LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
for (const auto& [child, variant] : root.mChildren) {
@@ -445,7 +475,7 @@
layerInfo->set_owner_uid(requestedState.ownerUid.val());
if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
- LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
+ LayerProtoHelper::writeToProto(snapshot.inputInfo,
[&]() { return layerInfo->mutable_input_window_info(); });
}
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 20c2260..41ea684 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -62,7 +62,7 @@
const renderengine::ExternalTexture& buffer,
std::function<perfetto::protos::ActiveBufferProto*()> getActiveBufferProto);
static void writeToProto(
- const gui::WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
+ const gui::WindowInfo& inputInfo,
std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto);
static void writeToProto(const mat4 matrix,
perfetto::protos::ColorTransformProto* colorTransformProto);
@@ -88,7 +88,12 @@
mLegacyLayers(legacyLayers),
mDisplayInfos(displayInfos),
mTraceFlags(traceFlags) {}
- perfetto::protos::LayersProto generate(const frontend::LayerHierarchy& root);
+ LayerProtoFromSnapshotGenerator& with(const frontend::LayerHierarchy& root);
+ // Creates a fake root and adds all offscreen layers from the passed in hierarchy to the fake
+ // root
+ LayerProtoFromSnapshotGenerator& withOffscreenLayers(
+ const frontend::LayerHierarchy& offscreenRoot);
+ perfetto::protos::LayersProto generate() { return mLayersProto; };
private:
void writeHierarchyToProto(const frontend::LayerHierarchy& root,
@@ -101,6 +106,8 @@
const frontend::DisplayInfos& mDisplayInfos;
uint32_t mTraceFlags;
perfetto::protos::LayersProto mLayersProto;
+ std::unordered_set<uint32_t> mVisitedLayers;
+
// winscope expects all the layers, so provide a snapshot even if it not currently drawing
std::unordered_map<frontend::LayerHierarchy::TraversalPath, frontend::LayerSnapshot,
frontend::LayerHierarchy::TraversalPathHash>
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index f52e60d..13e054e 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -45,51 +45,12 @@
const auto& lState = l->getDrawingState();
const auto& rState = r->getDrawingState();
- const auto ls = lState.layerStack;
- const auto rs = rState.layerStack;
- if (ls != rs)
- return (ls > rs) ? 1 : -1;
-
- int32_t lz = lState.z;
- int32_t rz = rState.z;
- if (lz != rz)
- return (lz > rz) ? 1 : -1;
-
if (l->sequence == r->sequence)
return 0;
return (l->sequence > r->sequence) ? 1 : -1;
}
-void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) const {
- for (size_t i = 0; i < size(); i++) {
- const auto& layer = (*this)[i];
- auto& state = layer->getDrawingState();
- if (state.isRelativeOf) {
- continue;
- }
- layer->traverseInZOrder(stateSet, visitor);
- }
-}
-
-void LayerVector::traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const {
- for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
- const auto& layer = (*this)[i];
- auto& state = layer->getDrawingState();
- if (state.isRelativeOf) {
- continue;
- }
- layer->traverseInReverseZOrder(stateSet, visitor);
- }
-}
-
-void LayerVector::traverse(const Visitor& visitor) const {
- for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
- const auto& layer = (*this)[i];
- layer->traverse(mStateSet, visitor);
- }
-}
-
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index a531f4f..38dc11d 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -46,11 +46,8 @@
// Sorts layer by layer-stack, Z order, and finally creation order (sequence).
int do_compare(const void* lhs, const void* rhs) const override;
-
using Visitor = std::function<void(Layer*)>;
- void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
- void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
- void traverse(const Visitor& visitor) const;
+
private:
const StateSet mStateSet;
};
diff --git a/services/surfaceflinger/LocklessQueue.h b/services/surfaceflinger/LocklessQueue.h
index 6b63360..4d0b261 100644
--- a/services/surfaceflinger/LocklessQueue.h
+++ b/services/surfaceflinger/LocklessQueue.h
@@ -15,11 +15,11 @@
*/
#pragma once
+
#include <atomic>
#include <optional>
-template <typename T>
-// Single consumer multi producer stack. We can understand the two operations independently to see
+// Single consumer multi producer queue. We can understand the two operations independently to see
// why they are without race condition.
//
// push is responsible for maintaining a linked list stored in mPush, and called from multiple
@@ -36,33 +36,27 @@
// then store the list and pop one element.
//
// If we already had something in the pop list we just pop directly.
+template <typename T>
class LocklessQueue {
public:
- class Entry {
- public:
- T mValue;
- std::atomic<Entry*> mNext;
- Entry(T value) : mValue(value) {}
- };
- std::atomic<Entry*> mPush = nullptr;
- std::atomic<Entry*> mPop = nullptr;
bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }
void push(T value) {
- Entry* entry = new Entry(value);
+ Entry* entry = new Entry(std::move(value));
Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
do {
entry->mNext = previousHead;
} while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
}
+
std::optional<T> pop() {
Entry* popped = mPop.load(/*std::memory_order_acquire*/);
if (popped) {
// Single consumer so this is fine
mPop.store(popped->mNext /* , std::memory_order_release */);
- auto value = popped->mValue;
+ auto value = std::move(popped->mValue);
delete popped;
- return std::move(value);
+ return value;
} else {
Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */);
if (!grabbedList) return std::nullopt;
@@ -74,9 +68,19 @@
grabbedList = next;
}
mPop.store(popped /* , std::memory_order_release */);
- auto value = grabbedList->mValue;
+ auto value = std::move(grabbedList->mValue);
delete grabbedList;
- return std::move(value);
+ return value;
}
}
+
+private:
+ class Entry {
+ public:
+ T mValue;
+ std::atomic<Entry*> mNext;
+ Entry(T value) : mValue(value) {}
+ };
+ std::atomic<Entry*> mPush = nullptr;
+ std::atomic<Entry*> mPop = nullptr;
};
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index c77bcfa..06c2f26 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -26,6 +26,7 @@
#include "RegionSamplingThread.h"
+#include <common/trace.h>
#include <compositionengine/Display.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <cutils/properties.h>
@@ -34,7 +35,6 @@
#include <gui/SyncScreenCaptureListener.h>
#include <renderengine/impl/ExternalTexture.h>
#include <ui/DisplayStatInfo.h>
-#include <utils/Trace.h>
#include <string>
@@ -148,7 +148,7 @@
std::lock_guard lock(mThreadControlMutex);
if (mSampleRequestTime.has_value()) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
mSampleRequestTime.reset();
mFlinger.scheduleSample();
}
@@ -166,7 +166,7 @@
if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
// content changed, but we sampled not too long ago, so we need to sample some time in the
// future.
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
+ SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
mSampleRequestTime = now;
return;
}
@@ -175,13 +175,13 @@
// until the next vsync deadline, defer this sampling work
// to a later frame, when hopefully there will be more time.
if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
+ SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
mSampleRequestTime = mSampleRequestTime.value_or(now);
return;
}
}
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
+ SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
mSampleRequestTime.reset();
mLastSampleTime = now;
@@ -247,7 +247,7 @@
}
void RegionSamplingThread::captureSample() {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard lock(mSamplingMutex);
if (mDescriptors.empty()) {
@@ -346,6 +346,7 @@
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
constexpr bool kIsProtected = false;
+ constexpr bool kAttachGainmap = false;
SurfaceFlinger::RenderAreaBuilderVariant
renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds,
@@ -356,18 +357,17 @@
if (FlagManager::getInstance().single_hop_screenshot() &&
FlagManager::getInstance().ce_fence_promise() && mFlinger.mRenderEngine->isThreaded()) {
std::vector<sp<LayerFE>> layerFEs;
- auto displayState =
- mFlinger.getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder,
- getLayerSnapshotsFn, layerFEs);
- fenceResult =
- mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
- kIsProtected, nullptr, displayState, layerFEs)
- .get();
+ auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder,
+ getLayerSnapshotsFn, layerFEs);
+ fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling,
+ kGrayscale, kIsProtected, kAttachGainmap, nullptr,
+ displayState, layerFEs)
+ .get();
} else {
- fenceResult =
- mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer,
- kRegionSampling, kGrayscale, kIsProtected, nullptr)
- .get();
+ fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn,
+ buffer, kRegionSampling, kGrayscale,
+ kIsProtected, kAttachGainmap, nullptr)
+ .get();
}
if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
@@ -394,7 +394,7 @@
}
mCachedBuffer = buffer;
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
+ SFTRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
}
// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 034e467..aa66ccf 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -39,21 +39,6 @@
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill) {}
- static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
- std::function<void(const LayerVector::Visitor&)> traverseLayers) {
- return [traverseLayers = std::move(traverseLayers)]() {
- std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- traverseLayers([&](Layer* layer) {
- // Layer::prepareClientComposition uses the layer's snapshot to populate the
- // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings
- // are generated with the layer's current buffer and geometry.
- layer->updateSnapshot(true /* updateGeometry */);
- layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
- });
- return layers;
- };
- }
-
virtual ~RenderArea() = default;
// Returns true if the render area is secure. A secure layer should be
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 6b65449..218c56e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -33,7 +33,7 @@
#include <android-base/stringprintf.h>
#include <binder/IPCThreadState.h>
-
+#include <common/trace.h>
#include <cutils/compiler.h>
#include <cutils/sched_policy.h>
@@ -41,7 +41,6 @@
#include <gui/SchedulingPolicy.h>
#include <utils/Errors.h>
-#include <utils/Trace.h>
#include <common/FlagManager.h>
#include <scheduler/VsyncConfig.h>
@@ -226,14 +225,14 @@
}
binder::Status EventThreadConnection::requestNextVsync() {
- ATRACE_CALL();
+ SFTRACE_CALL();
mEventThread->requestNextVsync(sp<EventThreadConnection>::fromExisting(this));
return binder::Status::ok();
}
binder::Status EventThreadConnection::getLatestVsyncEventData(
ParcelableVsyncEventData* outVsyncEventData) {
- ATRACE_CALL();
+ SFTRACE_CALL();
outVsyncEventData->vsync =
mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this),
systemTime());
@@ -321,7 +320,8 @@
mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .lastVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns(),
+ .committedVsyncOpt = mLastCommittedVsyncTime.ns()});
}
sp<EventThreadConnection> EventThread::createEventConnection(
@@ -528,10 +528,11 @@
}
if (mState == State::VSync) {
- const auto scheduleResult =
- mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
- .readyDuration = mReadyDuration.count(),
- .lastVsync = mLastVsyncCallbackTime.ns()});
+ const auto scheduleResult = mVsyncRegistration.schedule(
+ {.workDuration = mWorkDuration.get().count(),
+ .readyDuration = mReadyDuration.count(),
+ .lastVsync = mLastVsyncCallbackTime.ns(),
+ .committedVsyncOpt = mLastCommittedVsyncTime.ns()});
LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback");
} else {
mVsyncRegistration.cancel();
@@ -726,8 +727,9 @@
}
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
FlagManager::getInstance().vrr_config()) {
- mCallback.onExpectedPresentTimePosted(
- TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime()));
+ mLastCommittedVsyncTime =
+ TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime());
+ mCallback.onExpectedPresentTimePosted(mLastCommittedVsyncTime);
}
}
@@ -745,9 +747,12 @@
const auto relativeLastCallTime =
ticks<std::milli, float>(mLastVsyncCallbackTime - TimePoint::now());
+ const auto relativeLastCommittedTime =
+ ticks<std::milli, float>(mLastCommittedVsyncTime - TimePoint::now());
StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
mWorkDuration.get().count() / 1e6f, mReadyDuration.count() / 1e6f);
StringAppendF(&result, "%.2fms relative to now\n", relativeLastCallTime);
+ StringAppendF(&result, " with vsync committed at %.2fms", relativeLastCommittedTime);
StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size());
for (const auto& event : mPendingEvents) {
@@ -795,7 +800,8 @@
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .lastVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns(),
+ .committedVsyncOpt = mLastCommittedVsyncTime.ns()});
}
return oldRegistration;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index f772126..bbe4f9d 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -220,6 +220,7 @@
std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex);
std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule GUARDED_BY(mMutex);
TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now();
+ TimePoint mLastCommittedVsyncTime GUARDED_BY(mMutex) = TimePoint::now();
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index eae8d6d..d02d149 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -28,7 +28,6 @@
virtual void requestHardwareVsync(PhysicalDisplayId, bool enabled) = 0;
virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
- virtual void triggerOnFrameRateOverridesChanged() = 0;
virtual void onChoreographerAttached() = 0;
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index a819b79..64b85c0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -21,8 +21,8 @@
#include "LayerHistory.h"
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <cutils/properties.h>
-#include <gui/TraceUtils.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -72,7 +72,7 @@
void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) {
const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
- ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+ SFTRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
};
traceType(LayerHistory::LayerVoteType::NoVote, 1);
@@ -109,12 +109,12 @@
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) {
+void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled,
+ FrameRateCompatibility frameRateCompatibility) {
std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound,
"%s already registered", layer->getName().c_str());
- LayerVoteType type =
- getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled);
+ LayerVoteType type = getVoteType(frameRateCompatibility, contentDetectionEnabled);
auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
// The layer can be placed on either map, it is assumed that partitionLayers() will be called
@@ -190,7 +190,7 @@
}
auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
- ATRACE_CALL();
+ SFTRACE_CALL();
Summary summary;
std::lock_guard lock(mLock);
@@ -204,7 +204,7 @@
ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
layerFocused ? "" : "not");
- ATRACE_FORMAT("%s", info->getName().c_str());
+ SFTRACE_FORMAT("%s", info->getName().c_str());
const auto votes = info->getRefreshRateVote(selector, now);
for (LayerInfo::LayerVote vote : votes) {
if (vote.isNoVote()) {
@@ -222,8 +222,8 @@
const std::string categoryString = vote.category == FrameRateCategory::Default
? ""
: base::StringPrintf("category=%s", ftl::enum_string(vote.category).c_str());
- ATRACE_FORMAT_INSTANT("%s %s %s (%.2f)", ftl::enum_string(vote.type).c_str(),
- to_string(vote.fps).c_str(), categoryString.c_str(), weight);
+ SFTRACE_FORMAT_INSTANT("%s %s %s (%.2f)", ftl::enum_string(vote.type).c_str(),
+ to_string(vote.fps).c_str(), categoryString.c_str(), weight);
summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
vote.seamlessness, vote.category, vote.categorySmoothSwitchOnly,
weight, layerFocused});
@@ -238,7 +238,7 @@
}
void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const nsecs_t threshold = getActiveLayerThreshold(now);
// iterate over inactive map
@@ -310,7 +310,7 @@
if (gameModeFrameRateOverride.isValid()) {
info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
- ATRACE_FORMAT_INSTANT("GameModeFrameRateOverride");
+ SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride");
if (CC_UNLIKELY(mTraceEnabled)) {
trace(*info, gameFrameRateOverrideVoteType,
gameModeFrameRateOverride.getIntValue());
@@ -326,19 +326,19 @@
} else if (gameDefaultFrameRateOverride.isValid()) {
info->setLayerVote(
{gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride});
- ATRACE_FORMAT_INSTANT("GameDefaultFrameRateOverride");
+ SFTRACE_FORMAT_INSTANT("GameDefaultFrameRateOverride");
if (CC_UNLIKELY(mTraceEnabled)) {
trace(*info, gameFrameRateOverrideVoteType,
gameDefaultFrameRateOverride.getIntValue());
}
} else {
if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) {
- ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
- "%s %s",
- info->getName().c_str(),
- ftl::enum_string(frameRate.vote.type).c_str(),
- to_string(frameRate.vote.rate).c_str(),
- ftl::enum_string(frameRate.category).c_str());
+ SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
+ "%s %s",
+ info->getName().c_str(),
+ ftl::enum_string(frameRate.vote.type).c_str(),
+ to_string(frameRate.vote.rate).c_str(),
+ ftl::enum_string(frameRate.category).c_str());
}
info->resetLayerVote();
}
@@ -349,12 +349,12 @@
frameRate.vote.seamlessness, frameRate.category});
} else {
if (!frameRate.isVoteValidForMrr(isVrrDevice)) {
- ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
- "%s %s",
- info->getName().c_str(),
- ftl::enum_string(frameRate.vote.type).c_str(),
- to_string(frameRate.vote.rate).c_str(),
- ftl::enum_string(frameRate.category).c_str());
+ SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
+ "%s %s",
+ info->getName().c_str(),
+ ftl::enum_string(frameRate.vote.type).c_str(),
+ to_string(frameRate.vote.rate).c_str(),
+ ftl::enum_string(frameRate.category).c_str());
}
info->resetLayerVote();
}
@@ -421,7 +421,7 @@
bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea, float threshold) const {
const float ratio = (float)dirtyArea / mDisplayArea;
const bool isSmallDirty = ratio <= threshold;
- ATRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio);
+ SFTRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio);
return isSmallDirty;
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index c09f148..e3babba 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -51,7 +51,8 @@
~LayerHistory();
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, bool contentDetectionEnabled);
+ void registerLayer(Layer*, bool contentDetectionEnabled,
+ FrameRateCompatibility frameRateCompatibility);
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 632f42a..ff1926e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -27,10 +27,10 @@
#include <utility>
#include <android/native_window.h>
+#include <common/trace.h>
#include <cutils/compiler.h>
#include <cutils/trace.h>
#include <ftl/enum.h>
-#include <gui/TraceUtils.h>
#include <system/window.h>
#undef LOG_TAG
@@ -259,7 +259,7 @@
}
if (smallDirtyCount > 0) {
- ATRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount);
+ SFTRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount);
}
if (numDeltas == 0) {
@@ -272,7 +272,7 @@
std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector,
nsecs_t now) {
- ATRACE_CALL();
+ SFTRACE_CALL();
static constexpr float MARGIN = 1.0f; // 1Hz
if (!hasEnoughDataForHeuristic()) {
ALOGV("Not enough data");
@@ -307,7 +307,7 @@
LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
nsecs_t now) {
- ATRACE_CALL();
+ SFTRACE_CALL();
LayerInfo::RefreshRateVotes votes;
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
@@ -315,8 +315,8 @@
const auto voteType = mLayerVote.type == LayerHistory::LayerVoteType::NoVote
? LayerHistory::LayerVoteType::NoVote
: LayerHistory::LayerVoteType::ExplicitCategory;
- ATRACE_FORMAT_INSTANT("Vote %s (category=%s)", ftl::enum_string(voteType).c_str(),
- ftl::enum_string(mLayerVote.category).c_str());
+ SFTRACE_FORMAT_INSTANT("Vote %s (category=%s)", ftl::enum_string(voteType).c_str(),
+ ftl::enum_string(mLayerVote.category).c_str());
ALOGV("%s voted %s with category: %s", mName.c_str(),
ftl::enum_string(voteType).c_str(),
ftl::enum_string(mLayerVote.category).c_str());
@@ -326,7 +326,7 @@
if (mLayerVote.fps.isValid() ||
mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) {
- ATRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str());
+ SFTRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str());
ALOGV("%s voted %d", mName.c_str(), static_cast<int>(mLayerVote.type));
votes.push_back({mLayerVote.type, mLayerVote.fps, mLayerVote.seamlessness,
FrameRateCategory::Default, mLayerVote.categorySmoothSwitchOnly});
@@ -336,7 +336,7 @@
}
if (isAnimating(now)) {
- ATRACE_FORMAT_INSTANT("animating");
+ SFTRACE_FORMAT_INSTANT("animating");
ALOGV("%s is animating", mName.c_str());
mLastRefreshRate.animating = true;
votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
@@ -345,7 +345,7 @@
// Vote for max refresh rate whenever we're front-buffered.
if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) {
- ATRACE_FORMAT_INSTANT("front buffered");
+ SFTRACE_FORMAT_INSTANT("front buffered");
ALOGV("%s is front-buffered", mName.c_str());
votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
return votes;
@@ -354,7 +354,7 @@
const LayerInfo::Frequent frequent = isFrequent(now);
mIsFrequencyConclusive = frequent.isConclusive;
if (!frequent.isFrequent) {
- ATRACE_FORMAT_INSTANT("infrequent");
+ SFTRACE_FORMAT_INSTANT("infrequent");
ALOGV("%s is infrequent", mName.c_str());
mLastRefreshRate.infrequent = true;
mLastSmallDirtyCount = 0;
@@ -365,14 +365,14 @@
}
if (frequent.clearHistory) {
- ATRACE_FORMAT_INSTANT("frequent.clearHistory");
+ SFTRACE_FORMAT_INSTANT("frequent.clearHistory");
ALOGV("%s frequent.clearHistory", mName.c_str());
clearHistory(now);
}
// Return no vote if the recent frames are small dirty.
if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) {
- ATRACE_FORMAT_INSTANT("NoVote (small dirty)");
+ SFTRACE_FORMAT_INSTANT("NoVote (small dirty)");
ALOGV("%s is small dirty", mName.c_str());
votes.push_back({LayerHistory::LayerVoteType::NoVote, Fps()});
return votes;
@@ -380,13 +380,13 @@
auto refreshRate = calculateRefreshRateIfPossible(selector, now);
if (refreshRate.has_value()) {
- ATRACE_FORMAT_INSTANT("calculated (%s)", to_string(*refreshRate).c_str());
+ SFTRACE_FORMAT_INSTANT("calculated (%s)", to_string(*refreshRate).c_str());
ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
votes.push_back({LayerHistory::LayerVoteType::Heuristic, refreshRate.value()});
return votes;
}
- ATRACE_FORMAT_INSTANT("Max (can't resolve refresh rate)");
+ SFTRACE_FORMAT_INSTANT("Max (can't resolve refresh rate)");
ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
return votes;
@@ -452,7 +452,7 @@
mHeuristicTraceTagData = makeHeuristicTraceTagData();
}
- ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
+ SFTRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
}
return selectRefreshRate(selector);
@@ -486,9 +486,9 @@
mHeuristicTraceTagData = makeHeuristicTraceTagData();
}
- ATRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue());
- ATRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue());
- ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+ SFTRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue());
+ SFTRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue());
+ SFTRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
}
return consistent ? maxClosestRate : Fps();
@@ -595,6 +595,11 @@
return true;
}
+ if (category == FrameRateCategory::NoPreference && vote.rate.isValid() &&
+ vote.type == FrameRateCompatibility::ExactOrMultiple) {
+ return true;
+ }
+
return false;
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index ff88d71..2e1f938 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -57,7 +57,7 @@
mHandler(std::move(handler)) {}
void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
- ATRACE_CALL();
+ SFTRACE_CALL();
// Trace VSYNC-sf
mVsync.value = (mVsync.value + 1) % 2;
@@ -136,7 +136,7 @@
}
void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
mVsync.scheduledFrameTimeOpt =
@@ -188,12 +188,13 @@
postMessage(sp<ConfigureHandler>::make(mCompositor));
}
-void MessageQueue::scheduleFrame() {
- ATRACE_CALL();
+void MessageQueue::scheduleFrame(Duration workDurationSlack) {
+ SFTRACE_CALL();
std::lock_guard lock(mVsync.mutex);
+ const auto workDuration = Duration(mVsync.workDuration.get() - workDurationSlack);
mVsync.scheduledFrameTimeOpt =
- mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ mVsync.registration->schedule({.workDuration = workDuration.ns(),
.readyDuration = 0,
.lastVsync = mVsync.lastCallbackTime.ns()});
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index c5fc371..ba1efbe 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -74,7 +74,7 @@
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0;
virtual void scheduleConfigure() = 0;
- virtual void scheduleFrame() = 0;
+ virtual void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) = 0;
virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0;
};
@@ -149,7 +149,7 @@
void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override;
void scheduleConfigure() override;
- void scheduleFrame() override;
+ void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) override;
std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index a4368a6..ab9014e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -28,13 +28,12 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/match.h>
#include <ftl/unit.h>
-#include <gui/TraceUtils.h>
#include <scheduler/FrameRateMode.h>
-#include <utils/Trace.h>
#include "RefreshRateSelector.h"
@@ -494,7 +493,7 @@
GlobalSignals signals, Fps pacesetterFps) const
-> RankedFrameRates {
using namespace fps_approx_ops;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("%s: %zu layers", __func__, layers.size());
const auto& activeMode = *getActiveModeLocked().modePtr;
@@ -508,8 +507,8 @@
});
if (!ranking.empty()) {
- ATRACE_FORMAT_INSTANT("%s (Follower display)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (Follower display)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals, pacesetterFps};
}
@@ -521,8 +520,8 @@
if (signals.powerOnImminent) {
ALOGV("Power On Imminent");
const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending);
- ATRACE_FORMAT_INSTANT("%s (Power On Imminent)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (Power On Imminent)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, GlobalSignals{.powerOnImminent = true}};
}
@@ -608,8 +607,8 @@
if (signals.touch && !hasExplicitVoteLayers) {
ALOGV("Touch Boost");
const auto ranking = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
- ATRACE_FORMAT_INSTANT("%s (Touch Boost)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (Touch Boost)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, GlobalSignals{.touch = true}};
}
@@ -620,26 +619,27 @@
!(policy->primaryRangeIsSingleRate() && hasExplicitVoteLayers)) {
ALOGV("Idle");
const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
- ATRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, GlobalSignals{.idle = true}};
}
if (layers.empty() || noVoteLayers == layers.size()) {
ALOGV("No layers with votes");
const auto ranking = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
- ATRACE_FORMAT_INSTANT("%s (No layers with votes)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (No layers with votes)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
// If all layers are category NoPreference, use the current config.
if (noPreferenceLayers + noVoteLayers == layers.size()) {
ALOGV("All layers NoPreference");
- const auto ascendingWithPreferred =
- rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
- ATRACE_FORMAT_INSTANT("%s (All layers NoPreference)",
- to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
- return {ascendingWithPreferred, kNoSignals};
+ constexpr float kScore = std::numeric_limits<float>::max();
+ FrameRateRanking currentMode;
+ currentMode.emplace_back(ScoredFrameRate{getActiveModeLocked(), kScore});
+ SFTRACE_FORMAT_INSTANT("%s (All layers NoPreference)",
+ to_string(currentMode.front().frameRateMode.fps).c_str());
+ return {currentMode, kNoSignals};
}
const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0;
@@ -653,8 +653,8 @@
return !smoothSwitchOnly ||
mode.modePtr->getId() == activeModeId;
});
- ATRACE_FORMAT_INSTANT("%s (All layers Min)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (All layers Min)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
@@ -842,19 +842,19 @@
});
// TODO(b/364651864): Evaluate correctness of primaryRangeIsSingleRate.
- if (!isVrrDevice() && policy->primaryRangeIsSingleRate()) {
+ if (!mIsVrrDevice.load() && policy->primaryRangeIsSingleRate()) {
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (noLayerScore) {
ALOGV("Layers not scored");
const auto descending = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
- ATRACE_FORMAT_INSTANT("%s (Layers not scored)",
- to_string(descending.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (Layers not scored)",
+ to_string(descending.front().frameRateMode.fps).c_str());
return {descending, kNoSignals};
} else {
ALOGV("primaryRangeIsSingleRate");
- ATRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
}
@@ -890,7 +890,7 @@
if (scores.front().frameRateMode.fps <= touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost [late]");
- ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
+ SFTRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
to_string(touchRefreshRates.front().frameRateMode.fps).c_str());
return {touchRefreshRates, GlobalSignals{.touch = true}};
}
@@ -902,13 +902,13 @@
ALOGV("preferredDisplayMode");
const auto ascendingWithPreferred =
rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
- ATRACE_FORMAT_INSTANT("%s (preferredDisplayMode)",
- to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (preferredDisplayMode)",
+ to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
return {ascendingWithPreferred, kNoSignals};
}
ALOGV("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str());
- ATRACE_FORMAT_INSTANT("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str());
+ SFTRACE_FORMAT_INSTANT("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
@@ -950,7 +950,7 @@
Fps displayRefreshRate,
GlobalSignals globalSignals) const
-> UidToFrameRateOverride {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) {
return {};
}
@@ -1065,12 +1065,12 @@
return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
});
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
- ATRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
- to_string(overrideFps).c_str(), uid);
- if (ATRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) {
+ SFTRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
+ to_string(overrideFps).c_str(), uid);
+ if (SFTRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) {
std::stringstream ss;
ss << "FrameRateOverride " << uid;
- ATRACE_INT(ss.str().c_str(), overrideFps.getIntValue());
+ SFTRACE_INT(ss.str().c_str(), overrideFps.getIntValue());
}
frameRateOverrides.emplace(uid, overrideFps);
}
@@ -1395,13 +1395,14 @@
const auto& idleScreenConfigOpt = getCurrentPolicyLocked()->idleScreenConfigOpt;
if (idleScreenConfigOpt != oldPolicy.idleScreenConfigOpt) {
if (!idleScreenConfigOpt.has_value()) {
- // fallback to legacy timer if existed, otherwise pause the old timer
- LOG_ALWAYS_FATAL_IF(!mIdleTimer);
- if (mConfig.legacyIdleTimerTimeout > 0ms) {
- mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout);
- mIdleTimer->resume();
- } else {
- mIdleTimer->pause();
+ if (mIdleTimer) {
+ // fallback to legacy timer if existed, otherwise pause the old timer
+ if (mConfig.legacyIdleTimerTimeout > 0ms) {
+ mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout);
+ mIdleTimer->resume();
+ } else {
+ mIdleTimer->pause();
+ }
}
} else if (idleScreenConfigOpt->timeoutMillis > 0) {
// create a new timer or reconfigure
@@ -1641,7 +1642,7 @@
case FrameRateCategory::Normal:
return FpsRange{60_Hz, 120_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 120_Hz};
+ return FpsRange{48_Hz, 120_Hz};
case FrameRateCategory::HighHint:
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 998b1b8..a398c01 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -210,6 +210,8 @@
// within the timeout of DisplayPowerTimer.
bool powerOnImminent = false;
+ bool shouldEmitEvent() const { return !idle; }
+
bool operator==(GlobalSignals other) const {
return touch == other.touch && idle == other.idle &&
powerOnImminent == other.powerOnImminent;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 60f11b8..ee811eb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -24,12 +24,12 @@
#include <android-base/stringprintf.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <common/trace.h>
#include <configstore/Utils.h>
#include <ftl/concat.h>
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/small_map.h>
-#include <gui/TraceUtils.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
#include <ui/DisplayMap.h>
@@ -38,7 +38,6 @@
#include <FrameTimeline/FrameTimeline.h>
#include <scheduler/interface/ICompositor.h>
-#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <functional>
@@ -46,16 +45,15 @@
#include <numeric>
#include <common/FlagManager.h>
-#include "../Layer.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
+#include "Layer.h"
#include "OneShotTimer.h"
#include "RefreshRateStats.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
#include "TimeStats/TimeStats.h"
-#include "VSyncTracker.h"
#include "VsyncConfiguration.h"
#include "VsyncController.h"
#include "VsyncSchedule.h"
@@ -122,6 +120,12 @@
demotePacesetterDisplay(kPromotionParams);
promotePacesetterDisplay(pacesetterId, kPromotionParams);
+
+ // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+ mVsyncModulator->cancelRefreshRateChange();
+
+ mVsyncConfiguration->reset();
+ updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
@@ -258,8 +262,8 @@
const auto period = pacesetterPtr->targeterPtr->target().expectedFrameDuration();
const auto skipDuration = Duration::fromNs(
static_cast<nsecs_t>(period.ns() * mPacesetterFrameDurationFractionToSkip));
- ATRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)",
- mPacesetterFrameDurationFractionToSkip * 100, skipDuration.ns());
+ SFTRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)",
+ mPacesetterFrameDurationFractionToSkip * 100, skipDuration.ns());
std::this_thread::sleep_for(skipDuration);
mPacesetterFrameDurationFractionToSkip = 0.f;
}
@@ -290,7 +294,7 @@
return true;
}
- ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
+ SFTRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), *frameRate);
}
@@ -355,10 +359,8 @@
if (cycle == Cycle::Render) {
mRenderEventThread = std::move(eventThread);
- mRenderEventConnection = mRenderEventThread->createEventConnection();
} else {
mLastCompositeEventThread = std::move(eventThread);
- mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection();
}
}
@@ -403,14 +405,20 @@
eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
}
-void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) {
- const bool supportsFrameRateOverrideByContent =
- pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
+void Scheduler::onFrameRateOverridesChanged() {
+ const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] {
+ std::scoped_lock lock(mDisplayLock);
+ const auto pacesetterOpt = pacesetterDisplayLocked();
+ LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+ const Display& pacesetter = *pacesetterOpt;
+ return std::make_pair(FTL_FAKE_GUARD(kMainThreadContext, *mPacesetterDisplayId),
+ pacesetter.selectorPtr->supportsAppFrameRateOverrideByContent());
+ }();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
- eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides));
+ eventThreadFor(Cycle::Render).onFrameRateOverridesChanged(pacesetterId, std::move(overrides));
}
void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId,
@@ -418,50 +426,52 @@
eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
}
-void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
- {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
+bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
+ const bool isPacesetter =
+ FTL_FAKE_GUARD(kMainThreadContext,
+ (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
+
+ if (isPacesetter) {
std::lock_guard<std::mutex> lock(mPolicyLock);
- // Cache the last reported modes for primary display.
- mPolicy.cachedModeChangedParams = {cycle, mode};
+ mPolicy.emittedModeOpt = mode;
// Invalidate content based refresh rate selection so it could be calculated
// again for the new refresh rate.
mPolicy.contentRequirements.clear();
}
- onNonPrimaryDisplayModeChanged(cycle, mode);
-}
-void Scheduler::dispatchCachedReportedMode() {
- // Check optional fields first.
- if (!mPolicy.modeOpt) {
- ALOGW("No mode ID found, not dispatching cached mode.");
- return;
- }
- if (!mPolicy.cachedModeChangedParams) {
- ALOGW("No mode changed params found, not dispatching cached mode.");
- return;
- }
-
- // If the mode is not the current mode, this means that a
- // mode change is in progress. In that case we shouldn't dispatch an event
- // as it will be dispatched when the current mode changes.
- if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
- return;
- }
-
- // If there is no change from cached mode, there is no need to dispatch an event
- if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
- return;
- }
-
- mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
- onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle,
- mPolicy.cachedModeChangedParams->mode);
-}
-
-void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
if (hasEventThreads()) {
- eventThreadFor(cycle).onModeChanged(mode);
+ eventThreadFor(Cycle::Render).onModeChanged(mode);
+ }
+
+ return isPacesetter;
+}
+#pragma clang diagnostic pop
+
+void Scheduler::emitModeChangeIfNeeded() {
+ if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
+ ALOGW("No mode change to emit");
+ return;
+ }
+
+ const auto& mode = *mPolicy.modeOpt;
+
+ if (mode != pacesetterSelectorPtr()->getActiveMode()) {
+ // A mode change is pending. The event will be emitted when the mode becomes active.
+ return;
+ }
+
+ if (mode == *mPolicy.emittedModeOpt) {
+ // The event was already emitted.
+ return;
+ }
+
+ mPolicy.emittedModeOpt = mode;
+
+ if (hasEventThreads()) {
+ eventThreadFor(Cycle::Render).onModeChanged(mode);
}
}
@@ -476,20 +486,20 @@
}
}
-void Scheduler::updatePhaseConfiguration(Fps refreshRate) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
+void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refreshRate) {
+ const bool isPacesetter =
+ FTL_FAKE_GUARD(kMainThreadContext,
+ (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
+ if (!isPacesetter) return;
+
mRefreshRateStats->setRefreshRate(refreshRate);
mVsyncConfiguration->setRefreshRateFps(refreshRate);
setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
refreshRate.getPeriod());
}
-
-void Scheduler::resetPhaseConfiguration(Fps refreshRate) {
- // Cancel the pending refresh rate change, if any, before updating the phase configuration.
- mVsyncModulator->cancelRefreshRateChange();
-
- mVsyncConfiguration->reset();
- updatePhaseConfiguration(refreshRate);
-}
+#pragma clang diagnostic pop
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
@@ -518,7 +528,7 @@
}
void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
@@ -552,12 +562,12 @@
void Scheduler::onHardwareVsyncRequest(PhysicalDisplayId id, bool enabled) {
static const auto& whence = __func__;
- ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
+ SFTRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
// On main thread to serialize reads/writes of pending hardware VSYNC state.
static_cast<void>(
schedule([=, this]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) {
- ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
+ SFTRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
if (const auto displayOpt = mDisplays.get(id)) {
auto& display = displayOpt->get();
@@ -639,7 +649,7 @@
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
- ATRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str());
+ SFTRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str());
const auto scheduleOpt =
(ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) {
return display.powerMode == hal::PowerMode::OFF
@@ -659,11 +669,12 @@
}
}
-void Scheduler::registerLayer(Layer* layer) {
+void Scheduler::registerLayer(Layer* layer, FrameRateCompatibility frameRateCompatibility) {
// If the content detection feature is off, we still keep the layer history,
// since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection));
+ mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection),
+ frameRateCompatibility);
}
void Scheduler::deregisterLayer(Layer* layer) {
@@ -702,7 +713,7 @@
const auto selectorPtr = pacesetterSelectorPtr();
if (!selectorPtr->canSwitch()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
applyPolicy(&Policy::contentRequirements, std::move(summary));
@@ -787,7 +798,7 @@
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
- ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
+ SFTRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
@@ -818,7 +829,7 @@
void Scheduler::idleTimerCallback(TimerState state) {
applyPolicy(&Policy::idleTimer, state);
- ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
+ SFTRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
void Scheduler::touchTimerCallback(TimerState state) {
@@ -830,12 +841,12 @@
if (applyPolicy(&Policy::touch, touch).touch) {
mLayerHistory.clear();
}
- ATRACE_INT("TouchState", static_cast<int>(touch));
+ SFTRACE_INT("TouchState", static_cast<int>(touch));
}
void Scheduler::displayPowerTimerCallback(TimerState state) {
applyPolicy(&Policy::displayPowerTimer, state);
- ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
+ SFTRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
void Scheduler::dump(utils::Dumper& dumper) const {
@@ -871,22 +882,19 @@
mRefreshRateStats->dump(dumper.out());
dumper.eol();
- {
- utils::Dumper::Section section(dumper, "Frame Targeting"sv);
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
- std::scoped_lock lock(mDisplayLock);
- ftl::FakeGuard guard(kMainThreadContext);
+ for (const auto& [id, display] : mDisplays) {
+ utils::Dumper::Section
+ section(dumper,
+ id == mPacesetterDisplayId
+ ? ftl::Concat("Pacesetter Display ", id.value).c_str()
+ : ftl::Concat("Follower Display ", id.value).c_str());
- for (const auto& [id, display] : mDisplays) {
- utils::Dumper::Section
- section(dumper,
- id == mPacesetterDisplayId
- ? ftl::Concat("Pacesetter Display ", id.value).c_str()
- : ftl::Concat("Follower Display ", id.value).c_str());
-
- display.targeterPtr->dump(dumper);
- dumper.eol();
- }
+ display.selectorPtr->dump(dumper);
+ display.targeterPtr->dump(dumper);
+ dumper.eol();
}
}
@@ -907,10 +915,17 @@
}
}
-bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
- std::scoped_lock lock(mPolicyLock);
- return updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
+void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ const bool changed = (std::scoped_lock(mPolicyLock),
+ updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate));
+
+ if (changed) {
+ onFrameRateOverridesChanged();
+ }
}
+#pragma clang diagnostic pop
bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
Fps displayRefreshRate) {
@@ -1004,7 +1019,7 @@
auto& layerChoreographers = choreographers->second;
layerChoreographers.frameRate = fps;
- ATRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+ SFTRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
ALOGV("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
auto it = layerChoreographers.connections.begin();
@@ -1086,13 +1101,13 @@
void Scheduler::updateAttachedChoreographers(
const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate) {
- ATRACE_CALL();
+ SFTRACE_CALL();
updateAttachedChoreographersInternal(layerHierarchy, displayRefreshRate, 0);
}
template <typename S, typename T>
auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::vector<display::DisplayModeRequest> modeRequests;
GlobalSignals consideredSignals;
@@ -1129,33 +1144,40 @@
for (auto& [id, choice] : modeChoices) {
modeRequests.emplace_back(
display::DisplayModeRequest{.mode = std::move(choice.mode),
- .emitEvent = !choice.consideredSignals.idle});
+ .emitEvent = choice.consideredSignals
+ .shouldEmitEvent()});
}
- frameRateOverridesChanged = updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps);
-
+ if (!FlagManager::getInstance().vrr_bugfix_dropped_frame()) {
+ frameRateOverridesChanged =
+ updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps);
+ }
if (mPolicy.modeOpt != modeOpt) {
mPolicy.modeOpt = modeOpt;
refreshRateChanged = true;
- } else {
- // We don't need to change the display mode, but we might need to send an event
- // about a mode change, since it was suppressed if previously considered idle.
- if (!consideredSignals.idle) {
- dispatchCachedReportedMode();
- }
+ } else if (consideredSignals.shouldEmitEvent()) {
+ // The mode did not change, but we may need to emit if DisplayModeRequest::emitEvent was
+ // previously false.
+ emitModeChangeIfNeeded();
}
}
if (refreshRateChanged) {
mSchedulerCallback.requestDisplayModes(std::move(modeRequests));
}
+
+ if (FlagManager::getInstance().vrr_bugfix_dropped_frame()) {
+ std::scoped_lock lock(mPolicyLock);
+ frameRateOverridesChanged =
+ updateFrameRateOverridesLocked(consideredSignals, mPolicy.modeOpt->fps);
+ }
if (frameRateOverridesChanged) {
- mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ onFrameRateOverridesChanged();
}
return consideredSignals;
}
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
- ATRACE_CALL();
+ SFTRACE_CALL();
DisplayModeChoiceMap modeChoices;
const auto globalSignals = makeGlobalSignals();
@@ -1249,6 +1271,8 @@
} else {
mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
}
+
+ onFrameRateOverridesChanged();
}
void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) {
@@ -1267,6 +1291,7 @@
}
mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
+ onFrameRateOverridesChanged();
}
void Scheduler::updateSmallAreaDetection(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 94583db..c88b563 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -145,22 +145,16 @@
Cycle, EventRegistrationFlags eventRegistration = {},
const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock);
- const sp<EventThreadConnection>& getEventConnection(Cycle cycle) const {
- return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection;
- }
-
enum class Hotplug { Connected, Disconnected };
void dispatchHotplug(PhysicalDisplayId, Hotplug);
void dispatchHotplugError(int32_t errorCode);
- void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock);
- void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&);
+ // Returns true if the PhysicalDisplayId is the pacesetter.
+ bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
- void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId);
-
void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
// Modifies work duration in the event thread.
@@ -189,8 +183,7 @@
}
}
- void updatePhaseConfiguration(Fps);
- void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext);
+ void updatePhaseConfiguration(PhysicalDisplayId, Fps);
const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
@@ -222,7 +215,7 @@
REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
- void registerLayer(Layer*);
+ void registerLayer(Layer*, FrameRateCompatibility);
void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
@@ -326,7 +319,7 @@
return mLayerHistory.getLayerFramerate(now, id);
}
- bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
+ void updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
// Returns true if the small dirty detection is enabled for the appId.
bool supportSmallDirtyDetection(int32_t appId) {
@@ -450,6 +443,9 @@
bool updateFrameRateOverridesLocked(GlobalSignals, Fps displayRefreshRate)
REQUIRES(mPolicyLock);
+
+ void onFrameRateOverridesChanged();
+
void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&,
Fps displayRefreshRate);
int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&,
@@ -457,7 +453,7 @@
void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&,
Fps fps) EXCLUDES(mChoreographerLock);
- void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
+ void emitModeChangeIfNeeded() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
// IEventThreadCallback overrides
bool throttleVsync(TimePoint, uid_t) override;
@@ -467,10 +463,7 @@
void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
std::unique_ptr<EventThread> mRenderEventThread;
- sp<EventThreadConnection> mRenderEventConnection;
-
std::unique_ptr<EventThread> mLastCompositeEventThread;
- sp<EventThreadConnection> mLastCompositeEventConnection;
std::atomic<nsecs_t> mLastResyncTime = 0;
@@ -583,13 +576,8 @@
// Chosen display mode.
ftl::Optional<FrameRateMode> modeOpt;
- struct ModeChangedParams {
- Cycle cycle;
- FrameRateMode mode;
- };
-
- // Parameters for latest dispatch of mode change event.
- std::optional<ModeChangedParams> cachedModeChangedParams;
+ // Display mode of latest emitted event.
+ std::optional<FrameRateMode> emittedModeOpt;
} mPolicy GUARDED_BY(mPolicyLock);
std::mutex mChoreographerLock;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 0c43ffb..8993c38 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -93,6 +93,8 @@
* readyDuration will typically be 0.
* @lastVsync: The targeted display time. This will be snapped to the closest
* predicted vsync time after lastVsync.
+ * @committedVsyncOpt: The display time that is committed to the callback as the
+ * target vsync time.
*
* callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
@@ -101,10 +103,11 @@
nsecs_t workDuration = 0;
nsecs_t readyDuration = 0;
nsecs_t lastVsync = 0;
+ std::optional<nsecs_t> committedVsyncOpt;
bool operator==(const ScheduleTiming& other) const {
return workDuration == other.workDuration && readyDuration == other.readyDuration &&
- lastVsync == other.lastVsync;
+ lastVsync == other.lastVsync && committedVsyncOpt == other.committedVsyncOpt;
}
bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 6d6b70d..1925f11 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -19,8 +19,8 @@
#include <vector>
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <ftl/concat.h>
-#include <gui/TraceUtils.h>
#include <log/log_main.h>
#include <scheduler/TimeKeeper.h>
@@ -45,14 +45,14 @@
}
void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
- if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
+ if (!SFTRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
return;
}
ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ",
ns2us(*entry.wakeupTime() - now), "us; VSYNC in ",
ns2us(*entry.targetVsync() - now), "us");
- ATRACE_FORMAT_INSTANT(trace.c_str());
+ SFTRACE_FORMAT_INSTANT(trace.c_str());
}
} // namespace
@@ -98,20 +98,21 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
+ SFTRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
now + timing.workDuration +
timing.readyDuration),
- timing.lastVsync);
+ timing.committedVsyncOpt.value_or(
+ timing.lastVsync));
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
- ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
- wouldSkipAVsyncTarget, wouldSkipAWakeup);
+ SFTRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
+ wouldSkipAVsyncTarget, wouldSkipAWakeup);
if (FlagManager::getInstance().dont_skip_on_early_ro()) {
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
nextVsyncTime = mArmedInfo->mActualVsyncTime;
@@ -154,13 +155,13 @@
bool const nextVsyncTooClose = mLastDispatchTime &&
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
if (alreadyDispatchedForVsync) {
- ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
+ SFTRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
*mLastDispatchTime);
}
if (nextVsyncTooClose) {
- ATRACE_FORMAT_INSTANT("nextVsyncTooClose");
+ SFTRACE_FORMAT_INSTANT("nextVsyncTooClose");
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
*mLastDispatchTime + currentPeriod);
}
@@ -172,7 +173,7 @@
VSyncDispatch::ScheduleTiming timing,
std::optional<ArmingInfo> armedInfo) const
-> ArmingInfo {
- ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
+ SFTRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration;
const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync);
@@ -188,8 +189,8 @@
armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
- ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
- wouldSkipAVsyncTarget, wouldSkipAWakeup);
+ SFTRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
+ wouldSkipAVsyncTarget, wouldSkipAWakeup);
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
return *armedInfo;
}
@@ -199,7 +200,7 @@
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
- ATRACE_NAME("VSyncDispatchTimerQueueEntry::update");
+ SFTRACE_NAME("VSyncDispatchTimerQueueEntry::update");
if (!mArmedInfo && !mWorkloadUpdateInfo) {
return;
}
@@ -208,9 +209,12 @@
const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
- ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
- " lastVsyncDelta=%" PRId64,
- workDelta, readyDelta, lastVsyncDelta);
+ const auto lastCommittedVsyncDelta =
+ mWorkloadUpdateInfo->committedVsyncOpt.value_or(mWorkloadUpdateInfo->lastVsync) -
+ mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync);
+ SFTRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
+ " lastVsyncDelta=%" PRId64 " committedVsyncDelta=%" PRId64,
+ workDelta, readyDelta, lastVsyncDelta, lastCommittedVsyncDelta);
mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
@@ -261,10 +265,14 @@
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
StringAppendF(&result,
- "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative "
- "to now\n",
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms "
+ "lastVsync: %.2fms relative to now "
+ "committedVsync: %.2fms relative to now\n",
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
- (mScheduleTiming.lastVsync - systemTime()) / 1e6f);
+ (mScheduleTiming.lastVsync - systemTime()) / 1e6f,
+ (mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync) -
+ systemTime()) /
+ 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
@@ -310,7 +318,7 @@
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
@@ -337,13 +345,13 @@
if (min && min < mIntendedWakeupTime) {
setTimer(*min, now);
} else {
- ATRACE_NAME("cancel timer");
+ SFTRACE_NAME("cancel timer");
cancelTimer();
}
}
void VSyncDispatchTimerQueue::timerCallback() {
- ATRACE_CALL();
+ SFTRACE_CALL();
struct Invocation {
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t vsyncTimestamp;
@@ -383,7 +391,7 @@
for (auto const& invocation : invocations) {
ftl::Concat trace(ftl::truncated<5>(invocation.callback->name()));
- ATRACE_FORMAT("%s: %s", __func__, trace.c_str());
+ SFTRACE_FORMAT("%s: %s", __func__, trace.c_str());
invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
invocation.deadlineTimestamp);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 1422cfa..6e36f02 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -30,10 +30,10 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <ftl/concat.h>
-#include <gui/TraceUtils.h>
#include <utils/Log.h>
#include "RefreshRateSelector.h"
@@ -77,7 +77,7 @@
}
inline void VSyncPredictor::traceInt64(const char* name, int64_t value) const {
- ATRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value);
+ SFTRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value);
}
inline size_t VSyncPredictor::next(size_t i) const {
@@ -89,7 +89,9 @@
}
bool VSyncPredictor::validate(nsecs_t timestamp) const {
+ SFTRACE_CALL();
if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
+ SFTRACE_INSTANT("timestamp valid (first)");
return true;
}
@@ -98,7 +100,11 @@
(timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent &&
percent <= (kMaxPercent - kOutlierTolerancePercent)) {
- ATRACE_FORMAT_INSTANT("timestamp is not aligned with model");
+ SFTRACE_FORMAT_INSTANT("timestamp not aligned with model. aValidTimestamp %.2fms ago"
+ ", timestamp %.2fms ago, idealPeriod=%.2 percent=%d",
+ (mClock->now() - aValidTimestamp) / 1e6f,
+ (mClock->now() - timestamp) / 1e6f,
+ idealPeriod() / 1e6f, percent);
return false;
}
@@ -109,7 +115,7 @@
const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod();
if (distancePercent < kOutlierTolerancePercent) {
// duplicate timestamp
- ATRACE_FORMAT_INSTANT("duplicate timestamp");
+ SFTRACE_FORMAT_INSTANT("duplicate timestamp");
return false;
}
return true;
@@ -135,7 +141,7 @@
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard lock(mMutex);
@@ -148,15 +154,18 @@
// Add the timestamp to mTimestamps before clearing it so we could
// update mKnownTimestamp based on the new timestamp.
mTimestamps.push_back(timestamp);
- clearTimestamps();
+
+ // Do not clear timelines as we don't want to break the phase while
+ // we are still learning.
+ clearTimestamps(/* clearTimelines */ false);
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
} else {
mKnownTimestamp = timestamp;
}
- ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
- (mClock->now() - *mKnownTimestamp) / 1e6f);
+ SFTRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
return false;
}
@@ -235,7 +244,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -245,7 +254,7 @@
auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -297,7 +306,7 @@
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
std::optional<nsecs_t> lastVsyncOpt) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard lock(mMutex);
const auto now = TimePoint::fromNs(mClock->now());
@@ -330,8 +339,8 @@
if (*vsyncOpt > mLastCommittedVsync) {
mLastCommittedVsync = *vsyncOpt;
- ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms",
- float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f);
+ SFTRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms",
+ float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f);
}
return vsyncOpt->ns();
@@ -360,7 +369,11 @@
purgeTimelines(now);
for (auto& timeline : mTimelines) {
- if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
+ const bool isVsyncValid = FlagManager::getInstance().vrr_bugfix_24q4()
+ ? timeline.isWithin(TimePoint::fromNs(vsync)) ==
+ VsyncTimeline::VsyncOnTimeline::Unique
+ : timeline.validUntil() && timeline.validUntil()->ns() > vsync;
+ if (isVsyncValid) {
return timeline.isVSyncInPhase(model, vsync, frameRate);
}
}
@@ -370,7 +383,7 @@
}
void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) {
- ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
+ SFTRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
std::lock_guard lock(mMutex);
const auto prevRenderRate = mRenderRateOpt;
@@ -378,7 +391,7 @@
const auto renderPeriodDelta =
prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0;
if (applyImmediately) {
- ATRACE_FORMAT_INSTANT("applyImmediately");
+ SFTRACE_FORMAT_INSTANT("applyImmediately");
while (mTimelines.size() > 1) {
mTimelines.pop_front();
}
@@ -390,13 +403,20 @@
const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() &&
mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs();
if (newRenderRateIsHigher) {
- ATRACE_FORMAT_INSTANT("newRenderRateIsHigher");
+ SFTRACE_FORMAT_INSTANT("newRenderRateIsHigher");
mTimelines.clear();
mLastCommittedVsync = TimePoint::fromNs(0);
} else {
- mTimelines.back().freeze(
- TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
+ if (FlagManager::getInstance().vrr_bugfix_24q4()) {
+ // We need to freeze the timeline at the committed vsync, and
+ // then use with threshold adjustments when required to avoid
+ // marginal errors when checking the vsync on the timeline.
+ mTimelines.back().freeze(mLastCommittedVsync);
+ } else {
+ mTimelines.back().freeze(
+ TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
+ }
}
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
purgeTimelines(TimePoint::fromNs(mClock->now()));
@@ -405,7 +425,7 @@
void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(),
"mode does not belong to the display");
- ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str());
+ SFTRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str());
const auto timeout = modePtr->getVrrConfig()
? modePtr->getVrrConfig()->notifyExpectedPresentConfig
: std::nullopt;
@@ -414,6 +434,9 @@
timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
std::lock_guard lock(mMutex);
+ // do not clear the timelines on VRR displays if we didn't change the mode
+ const bool isVrr = modePtr->getVrrConfig().has_value();
+ const bool clearTimelines = !isVrr || mDisplayModePtr->getId() != modePtr->getId();
mDisplayModePtr = modePtr;
mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
@@ -427,13 +450,15 @@
mRateMap[idealPeriod()] = {idealPeriod(), 0};
}
- mTimelines.clear();
- clearTimestamps();
+ if (clearTimelines) {
+ mTimelines.clear();
+ }
+ clearTimestamps(clearTimelines);
}
Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mNumVsyncsForFrame <= 1) {
return 0ns;
@@ -441,20 +466,21 @@
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
- const auto minFramePeriod = minFramePeriodLocked().ns();
+ const auto minFramePeriod = minFramePeriodLocked();
auto prev = lastConfirmedPresentTime.ns();
for (auto& current : mPastExpectedPresentTimes) {
if (CC_UNLIKELY(mTraceOn)) {
- ATRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
- static_cast<float>(current.ns() - lastConfirmedPresentTime.ns()) /
- 1e6f);
+ SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
+ static_cast<float>(current.ns() -
+ lastConfirmedPresentTime.ns()) /
+ 1e6f);
}
- const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod;
+ const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns();
if (minPeriodViolation) {
- ATRACE_NAME("minPeriodViolation");
- current = TimePoint::fromNs(prev + minFramePeriod);
+ SFTRACE_NAME("minPeriodViolation");
+ current = TimePoint::fromNs(prev + minFramePeriod.ns());
prev = current.ns();
} else {
break;
@@ -465,7 +491,7 @@
const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
if (phase > 0ns) {
for (auto& timeline : mTimelines) {
- timeline.shiftVsyncSequence(phase);
+ timeline.shiftVsyncSequence(phase, minFramePeriod);
}
mPastExpectedPresentTimes.clear();
return phase;
@@ -475,18 +501,18 @@
return 0ns;
}
-void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
- TimePoint lastConfirmedPresentTime) {
- ATRACE_NAME("VSyncPredictor::onFrameBegin");
+void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) {
+ SFTRACE_NAME("VSyncPredictor::onFrameBegin");
std::lock_guard lock(mMutex);
if (!mDisplayModePtr->getVrrConfig()) return;
+ const auto [lastConfirmedPresentTime, lastConfirmedExpectedPresentTime] = lastSignaledFrameTime;
if (CC_UNLIKELY(mTraceOn)) {
- ATRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence",
- static_cast<float>(expectedPresentTime.ns() -
- lastConfirmedPresentTime.ns()) /
- 1e6f);
+ SFTRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence",
+ static_cast<float>(expectedPresentTime.ns() -
+ lastConfirmedPresentTime.ns()) /
+ 1e6f);
}
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
@@ -497,9 +523,9 @@
const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
if (frontIsBeforeConfirmed) {
if (CC_UNLIKELY(mTraceOn)) {
- ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
- static_cast<float>(lastConfirmedPresentTime.ns() - front) /
- 1e6f);
+ SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
+ static_cast<float>(lastConfirmedPresentTime.ns() - front) /
+ 1e6f);
}
mPastExpectedPresentTimes.pop_front();
} else {
@@ -507,6 +533,11 @@
}
}
+ if (lastConfirmedExpectedPresentTime.ns() - lastConfirmedPresentTime.ns() > threshold) {
+ SFTRACE_FORMAT_INSTANT("lastFramePresentedEarly");
+ return;
+ }
+
const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
if (phase > 0ns) {
mMissedVsync = {expectedPresentTime, minFramePeriodLocked()};
@@ -514,7 +545,7 @@
}
void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
- ATRACE_NAME("VSyncPredictor::onFrameMissed");
+ SFTRACE_NAME("VSyncPredictor::onFrameMissed");
std::lock_guard lock(mMutex);
if (!mDisplayModePtr->getVrrConfig()) return;
@@ -539,15 +570,19 @@
return mRateMap.find(idealPeriod())->second;
}
-void VSyncPredictor::clearTimestamps() {
- ATRACE_CALL();
+void VSyncPredictor::clearTimestamps(bool clearTimelines) {
+ SFTRACE_FORMAT("%s: clearTimelines=%d", __func__, clearTimelines);
if (!mTimestamps.empty()) {
auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
if (mKnownTimestamp) {
mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
} else {
mKnownTimestamp = maxRb;
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp (maxRb) was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
}
mTimestamps.clear();
@@ -558,7 +593,7 @@
if (mTimelines.empty()) {
mLastCommittedVsync = TimePoint::fromNs(0);
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
- } else {
+ } else if (clearTimelines) {
while (mTimelines.size() > 1) {
mTimelines.pop_front();
}
@@ -579,9 +614,10 @@
}
void VSyncPredictor::resetModel() {
+ SFTRACE_CALL();
std::lock_guard lock(mMutex);
mRateMap[idealPeriod()] = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
}
void VSyncPredictor::dump(std::string& result) const {
@@ -602,7 +638,7 @@
if (mRenderRateOpt &&
mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase <
mClock->now()) {
- ATRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase");
+ SFTRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase");
mTimelines.clear();
mLastCommittedVsync = TimePoint::fromNs(0);
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
@@ -611,7 +647,10 @@
while (mTimelines.size() > 1) {
const auto validUntilOpt = mTimelines.front().validUntil();
- if (validUntilOpt && *validUntilOpt < now) {
+ const bool isTimelineOutDated = FlagManager::getInstance().vrr_bugfix_24q4()
+ ? mTimelines.front().isWithin(now) == VsyncTimeline::VsyncOnTimeline::Outside
+ : validUntilOpt && *validUntilOpt < now;
+ if (isTimelineOutDated) {
mTimelines.pop_front();
} else {
break;
@@ -635,16 +674,16 @@
void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
- ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f",
- mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA",
- float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f);
+ SFTRACE_FORMAT_INSTANT("renderRate %s valid for %.2f",
+ mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA",
+ float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f);
mValidUntil = lastVsync;
}
std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom(
Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsync,
MissedVsync missedVsync, std::optional<nsecs_t> lastVsyncOpt) {
- ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
+ SFTRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
const auto threshold = model.slope / 2;
@@ -656,34 +695,43 @@
if (lastFrameMissed) {
// If the last frame missed is the last vsync, we already shifted the timeline. Depends
// on whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a
- // different fixup. There is no need to to shift the vsync timeline again.
- vsyncTime += missedVsync.fixup.ns();
- ATRACE_FORMAT_INSTANT("lastFrameMissed");
+ // different fixup if we are violating the minFramePeriod.
+ // There is no need to shift the vsync timeline again.
+ if (vsyncTime - missedVsync.vsync.ns() < minFramePeriodOpt->ns()) {
+ vsyncTime += missedVsync.fixup.ns();
+ SFTRACE_FORMAT_INSTANT("lastFrameMissed");
+ }
} else if (mightBackpressure && lastVsyncOpt) {
- // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
- // first before trying to use it.
- lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ if (!FlagManager::getInstance().vrr_bugfix_24q4()) {
+ // lastVsyncOpt does not need to be corrected with the new rate, and
+ // it should be used as is to avoid skipping a frame when changing rates are
+ // aligned at vsync time.
+ lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ }
const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) {
// avoid a duplicate vsync
- ATRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f "
- "which "
- "is %.2f "
- "from "
- "prev. "
- "adjust by %.2f",
- static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
- static_cast<float>(vsyncDiff) / 1e6f,
- static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f);
+ SFTRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f "
+ "which "
+ "is %.2f "
+ "from "
+ "prev. "
+ "adjust by %.2f",
+ static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
+ static_cast<float>(vsyncDiff) / 1e6f,
+ static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f);
vsyncTime += mRenderRateOpt->getPeriodNsecs();
}
}
}
- ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
- if (mValidUntil && vsyncTime > mValidUntil->ns()) {
- ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
- static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
+ SFTRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
+ const bool isVsyncInvalid = FlagManager::getInstance().vrr_bugfix_24q4()
+ ? isWithin(TimePoint::fromNs(vsyncTime)) == VsyncOnTimeline::Outside
+ : mValidUntil && vsyncTime > mValidUntil->ns();
+ if (isVsyncInvalid) {
+ SFTRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
+ static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
return std::nullopt;
}
@@ -737,7 +785,9 @@
return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
};
- Fps displayFps = Fps::fromPeriodNsecs(mIdealPeriod.ns());
+ Fps displayFps = !FlagManager::getInstance().vrr_bugfix_24q4() && mRenderRateOpt
+ ? *mRenderRateOpt
+ : Fps::fromPeriodNsecs(mIdealPeriod.ns());
const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
const auto now = TimePoint::now();
@@ -745,18 +795,39 @@
return true;
}
const auto vsyncSequence = getVsyncSequenceLocked(model, vsync);
- ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu",
- getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor);
+ SFTRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu",
+ getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor);
return vsyncSequence.seq % divisor == 0;
}
-void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
+void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase, Period minFramePeriod) {
if (mLastVsyncSequence) {
- ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
+ const auto renderRate = mRenderRateOpt.value_or(Fps::fromPeriodNsecs(mIdealPeriod.ns()));
+ const auto threshold = mIdealPeriod.ns() / 2;
+ if (renderRate.getPeriodNsecs() - phase.ns() + threshold >= minFramePeriod.ns()) {
+ SFTRACE_FORMAT_INSTANT("Not-Adjusting vsync by %.2f",
+ static_cast<float>(phase.ns()) / 1e6f);
+ return;
+ }
+ SFTRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
mLastVsyncSequence->vsyncTime += phase.ns();
}
}
+VSyncPredictor::VsyncTimeline::VsyncOnTimeline VSyncPredictor::VsyncTimeline::isWithin(
+ TimePoint vsync) {
+ const auto threshold = mIdealPeriod.ns() / 2;
+ if (!mValidUntil || vsync.ns() < mValidUntil->ns() - threshold) {
+ // if mValidUntil is absent then timeline is not frozen and
+ // vsync should be unique to that timeline.
+ return VsyncOnTimeline::Unique;
+ }
+ if (vsync.ns() > mValidUntil->ns() + threshold) {
+ return VsyncOnTimeline::Outside;
+ }
+ return VsyncOnTimeline::Shared;
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 8ce61d8..2df3d04 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -22,6 +22,7 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <scheduler/FrameTime.h>
#include <scheduler/TimeKeeper.h>
#include <ui/DisplayId.h>
@@ -77,7 +78,7 @@
void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex);
- void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
+ void onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) final
EXCLUDES(mMutex);
void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex);
@@ -103,9 +104,16 @@
void freeze(TimePoint lastVsync);
std::optional<TimePoint> validUntil() const { return mValidUntil; }
bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
- void shiftVsyncSequence(Duration phase);
+ void shiftVsyncSequence(Duration phase, Period minFramePeriod);
void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
+ enum class VsyncOnTimeline {
+ Unique, // Within timeline, not shared with next timeline.
+ Shared, // Within timeline, shared with next timeline.
+ Outside, // Outside of the timeline.
+ };
+ VsyncOnTimeline isWithin(TimePoint vsync);
+
private:
nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
@@ -119,7 +127,7 @@
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
- void clearTimestamps() REQUIRES(mMutex);
+ void clearTimestamps(bool clearTimelines) REQUIRES(mMutex);
const std::unique_ptr<Clock> mClock;
const PhysicalDisplayId mId;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 8038364..b974cd2 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -20,11 +20,10 @@
//#define LOG_NDEBUG 0
#include <assert.h>
+#include <common/trace.h>
#include <cutils/properties.h>
#include <ftl/concat.h>
-#include <gui/TraceUtils.h>
#include <log/log.h>
-#include <utils/Trace.h>
#include "../TracedOrdinal.h"
#include "VSyncDispatch.h"
@@ -53,7 +52,7 @@
VSyncReactor::~VSyncReactor() = default;
bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!fence) {
return false;
@@ -66,8 +65,8 @@
std::lock_guard lock(mMutex);
if (mExternalIgnoreFences || mInternalIgnoreFences) {
- ATRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d",
- mExternalIgnoreFences, mInternalIgnoreFences);
+ SFTRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d",
+ mExternalIgnoreFences, mInternalIgnoreFences);
return true;
}
@@ -121,7 +120,7 @@
}
void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> modePtr) {
- ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
+ SFTRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
mPeriodConfirmationInProgress = true;
mModePtrTransitioningTo = modePtr.get();
mMoreSamplesNeeded = true;
@@ -129,19 +128,21 @@
}
void VSyncReactor::endPeriodTransition() {
- ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
+ SFTRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
mModePtrTransitioningTo.reset();
mPeriodConfirmationInProgress = false;
mLastHwVsync.reset();
}
void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) {
- ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(),
- modePtr->getVsyncRate().getPeriodNsecs());
+ SFTRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(),
+ modePtr->getVsyncRate().getPeriodNsecs());
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
+ // kernel idle timer is not applicable for VRR
+ const bool supportKernelIdleTimer = mSupportKernelIdleTimer && !modePtr->getVrrConfig();
+ if (!supportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
@@ -191,7 +192,7 @@
std::lock_guard lock(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
- ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value);
+ SFTRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value);
if (mModePtrTransitioningTo) {
mTracker.setDisplayModePtr(ftl::as_non_null(mModePtrTransitioningTo));
*periodFlushed = true;
@@ -205,12 +206,12 @@
endPeriodTransition();
mMoreSamplesNeeded = mTracker.needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
- ATRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value);
+ SFTRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value);
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
} else {
- ATRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value);
+ SFTRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value);
*periodFlushed = false;
mTracker.addVsyncTimestamp(timestamp);
mMoreSamplesNeeded = mTracker.needsMoreSamples();
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 134d28e..3376fad 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -21,6 +21,7 @@
#include <scheduler/Fps.h>
#include <scheduler/FrameRateMode.h>
+#include <scheduler/FrameTime.h>
#include "VSyncDispatch.h"
@@ -112,8 +113,7 @@
*/
virtual void setRenderRate(Fps, bool applyImmediately) = 0;
- virtual void onFrameBegin(TimePoint expectedPresentTime,
- TimePoint lastConfirmedPresentTime) = 0;
+ virtual void onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) = 0;
virtual void onFrameMissed(TimePoint expectedPresentTime) = 0;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 586357f..3c5f68c 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -21,9 +21,8 @@
#include "VsyncModulator.h"
-#include <android-base/properties.h>
+#include <common/trace.h>
#include <log/log.h>
-#include <utils/Trace.h>
#include <chrono>
#include <cinttypes>
@@ -37,8 +36,7 @@
VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
: mVsyncConfigSet(config),
- mNow(now),
- mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+ mNow(now) {}
VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -71,10 +69,6 @@
break;
}
- if (mTraceDetailedInfo) {
- ATRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size()));
- }
-
if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) {
mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
mEarlyTransactionStartTime = mNow();
@@ -167,15 +161,19 @@
const VsyncConfig& offsets = getNextVsyncConfig();
mVsyncConfig = offsets;
- if (mTraceDetailedInfo) {
- const bool isEarly = &offsets == &mVsyncConfigSet.early;
- const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
- const bool isLate = &offsets == &mVsyncConfigSet.late;
+ // Trace config type
+ SFTRACE_INT("Vsync-Early", &mVsyncConfig == &mVsyncConfigSet.early);
+ SFTRACE_INT("Vsync-EarlyGpu", &mVsyncConfig == &mVsyncConfigSet.earlyGpu);
+ SFTRACE_INT("Vsync-Late", &mVsyncConfig == &mVsyncConfigSet.late);
- ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
- ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
- ATRACE_INT("Vsync-LateOffsetsOn", isLate);
- }
+ // Trace early vsync conditions
+ SFTRACE_INT("EarlyWakeupRequests",
+ static_cast<int>(mEarlyWakeupRequests.size()));
+ SFTRACE_INT("EarlyTransactionFrames", mEarlyTransactionFrames);
+ SFTRACE_INT("RefreshRateChangePending", mRefreshRateChangePending);
+
+ // Trace early gpu conditions
+ SFTRACE_INT("EarlyGpuFrames", mEarlyGpuFrames);
return offsets;
}
@@ -183,7 +181,6 @@
void VsyncModulator::binderDied(const wp<IBinder>& who) {
std::lock_guard<std::mutex> lock(mMutex);
mEarlyWakeupRequests.erase(who);
-
static_cast<void>(updateVsyncConfigLocked());
}
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index be0d334..d0a7935 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -105,7 +105,6 @@
std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
const Now mNow;
- const bool mTraceDetailedInfo;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 2fa3318..d3e312a 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -18,8 +18,8 @@
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <ftl/fake_guard.h>
-#include <gui/TraceUtils.h>
#include <scheduler/Fps.h>
#include <scheduler/Timer.h>
@@ -182,7 +182,7 @@
}
void VsyncSchedule::enableHardwareVsyncLocked() {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mHwVsyncState == HwVsyncState::Disabled) {
getTracker().resetModel();
mRequestHardwareVsync(mId, true);
@@ -191,7 +191,7 @@
}
void VsyncSchedule::disableHardwareVsync(bool disallow) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mHwVsyncLock);
switch (mHwVsyncState) {
case HwVsyncState::Enabled:
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index a54d435..2185bb0 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -26,6 +26,7 @@
#include <ui/FenceTime.h>
#include <scheduler/Features.h>
+#include <scheduler/FrameTime.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
#include <scheduler/interface/CompositeResult.h>
@@ -54,31 +55,20 @@
std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
- // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
- TimePoint pastVsyncTime(Period minFramePeriod) const;
-
- // The present fence for the frame that had targeted the most recent VSYNC before this frame.
- // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
- // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
- // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
- // signaled by now (unless that frame missed).
- FenceTimePtr presentFenceForPastVsync(Period minFramePeriod) const;
-
- // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
- const FenceTimePtr& presentFenceForPreviousFrame() const {
- return mPresentFences.front().fenceTime;
- }
+ // Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead.
+ const FenceTimePtr& presentFenceForPreviousFrame() const;
bool isFramePending() const { return mFramePending; }
+ bool wouldBackpressureHwc() const { return mWouldBackpressureHwc; }
bool didMissFrame() const { return mFrameMissed; }
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
- TimePoint lastSignaledFrameTime() const { return mLastSignaledFrameTime; };
+ FrameTime lastSignaledFrameTime() const { return mLastSignaledFrameTime; }
protected:
explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
- bool wouldPresentEarly(Period minFramePeriod) const;
+ bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const;
// Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
@@ -87,8 +77,7 @@
void addFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime,
TimePoint expectedPresentTime) {
- mFenceWithFenceTimes.next() = {std::move(presentFence), presentFenceTime,
- expectedPresentTime};
+ mPresentFences.next() = {std::move(presentFence), presentFenceTime, expectedPresentTime};
}
VsyncId mVsyncId;
@@ -100,17 +89,25 @@
TracedOrdinal<bool> mFrameMissed;
TracedOrdinal<bool> mHwcFrameMissed;
TracedOrdinal<bool> mGpuFrameMissed;
+ bool mWouldBackpressureHwc = false;
- struct FenceWithFenceTime {
+ struct PresentFence {
sp<Fence> fence = Fence::NO_FENCE;
FenceTimePtr fenceTime = FenceTime::NO_FENCE;
TimePoint expectedPresentTime = TimePoint();
};
- // size should be longest sf-duration / shortest vsync period and round up
- std::array<FenceWithFenceTime, 5> mPresentFences; // currently consider 166hz.
- utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes;
- TimePoint mLastSignaledFrameTime;
+ // The present fence for the frame that had targeted the most recent VSYNC before this frame.
+ // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
+ // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
+ // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
+ // signaled by now (unless that frame missed).
+ std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence(
+ Period vsyncPeriod, Period minFramePeriod) const;
+ std::array<PresentFence, 2> mPresentFencesLegacy;
+ utils::RingBuffer<PresentFence, 5> mPresentFences;
+
+ FrameTime mLastSignaledFrameTime;
private:
friend class FrameTargeterTestBase;
@@ -120,33 +117,6 @@
static_assert(N > 1);
return expectedFrameDuration() > (N - 1) * minFramePeriod;
}
-
- const FenceTimePtr pastVsyncTimePtr() const {
- auto pastFenceTimePtr = FenceTime::NO_FENCE;
- for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) {
- const auto& [_, fenceTimePtr, expectedPresentTime] = mFenceWithFenceTimes[i];
- if (expectedPresentTime > mFrameBeginTime) {
- return pastFenceTimePtr;
- }
- pastFenceTimePtr = fenceTimePtr;
- }
- return pastFenceTimePtr;
- }
-
- size_t getPresentFenceShift(Period minFramePeriod) const {
- size_t shift = 0;
- if (minFramePeriod.ns() == 0) {
- return shift;
- }
- const bool isTwoVsyncsAhead = targetsVsyncsAhead<2>(minFramePeriod);
- if (isTwoVsyncsAhead) {
- shift = static_cast<size_t>(expectedFrameDuration().ns() / minFramePeriod.ns());
- if (shift >= mPresentFences.size()) {
- shift = mPresentFences.size() - 1;
- }
- }
- return shift;
- }
};
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
@@ -169,7 +139,7 @@
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
- std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod,
+ std::optional<TimePoint> computeEarliestPresentTime(Period vsyncPeriod, Period minFramePeriod,
Duration hwcMinWorkDuration);
// TODO(b/241285191): Merge with FrameTargeter::endFrame.
diff --git a/libs/tracing_perfetto/include/trace_result.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h
similarity index 76%
rename from libs/tracing_perfetto/include/trace_result.h
rename to services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h
index f7581fc..ed5c899 100644
--- a/libs/tracing_perfetto/include/trace_result.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h
@@ -14,17 +14,13 @@
* limitations under the License.
*/
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
+#pragma once
-namespace tracing_perfetto {
+#include <scheduler/Time.h>
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
+namespace android::scheduler {
+struct FrameTime {
+ TimePoint signalTime;
+ TimePoint expectedPresentTime;
};
-
-}
-
-#endif // TRACE_RESULT_H
+} // namespace android::scheduler
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 7036e67..3ee1e54 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-#include <gui/TraceUtils.h>
-
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/IVsyncSource.h>
+#include <utils/Log.h>
namespace android::scheduler {
+using namespace std::chrono_literals;
FrameTarget::FrameTarget(const std::string& displayLabel)
: mFramePending("PrevFramePending " + displayLabel, false),
@@ -28,34 +29,53 @@
mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
-TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const {
- // TODO(b/267315508): Generalize to N VSYNCs.
- const size_t shift = getPresentFenceShift(minFramePeriod);
- return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
-}
-
-FenceTimePtr FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
- if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
- return pastVsyncTimePtr();
+std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence>
+FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const {
+ if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
+ return {true, mPresentFencesLegacy[i]};
}
- const size_t shift = getPresentFenceShift(minFramePeriod);
- ATRACE_FORMAT("mPresentFences shift=%zu", shift);
- return mPresentFences[shift].fenceTime;
+ bool wouldBackpressure = true;
+ auto expectedPresentTime = mExpectedPresentTime;
+ for (size_t i = mPresentFences.size(); i != 0; --i) {
+ const auto& fence = mPresentFences[i - 1];
+
+ if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) {
+ wouldBackpressure = false;
+ }
+
+ if (fence.expectedPresentTime <= mFrameBeginTime) {
+ return {wouldBackpressure, fence};
+ }
+
+ expectedPresentTime = fence.expectedPresentTime;
+ }
+ return {wouldBackpressure, PresentFence{}};
}
-bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
- // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
- // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
-
- // TODO(b/267315508): Generalize to N VSYNCs.
- const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter();
- if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) {
+bool FrameTarget::wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const {
+ if (targetsVsyncsAhead<3>(minFramePeriod)) {
return true;
}
- const auto fence = presentFenceForPastVsync(minFramePeriod);
- return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ const auto [wouldBackpressure, fence] =
+ expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
+
+ return !wouldBackpressure ||
+ (fence.fenceTime->isValid() &&
+ fence.fenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING);
+}
+
+const FenceTimePtr& FrameTarget::presentFenceForPreviousFrame() const {
+ if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ if (mPresentFences.size() > 0) {
+ return mPresentFences.back().fenceTime;
+ }
+ return FenceTime::NO_FENCE;
+ }
+
+ return mPresentFencesLegacy.front().fenceTime;
}
void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
@@ -89,27 +109,39 @@
}
if (!mSupportsExpectedPresentTime) {
- mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration);
+ mEarliestPresentTime =
+ computeEarliestPresentTime(vsyncPeriod, minFramePeriod, args.hwcMinWorkDuration);
}
- ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
- ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
- mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
+ SFTRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
+ ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
+ mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
- const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod);
+ const auto [wouldBackpressure, fence] =
+ expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
// In cases where the present fence is about to fire, give it a small grace period instead of
// giving up on the frame.
- //
- // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
- // approximately equal, not whether backpressure propagation is enabled.
- const int graceTimeForPresentFenceMs = static_cast<int>(
- mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
+ const int graceTimeForPresentFenceMs = [&] {
+ const bool considerBackpressure =
+ mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu);
+
+ if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ return static_cast<int>(considerBackpressure);
+ }
+
+ if (!wouldBackpressure || !considerBackpressure) {
+ return 0;
+ }
+
+ return static_cast<int>((std::abs(fence.expectedPresentTime.ns() - mFrameBeginTime.ns()) <=
+ Duration(1ms).ns()));
+ }();
// Pending frames may trigger backpressure propagation.
const auto& isFencePending = *isFencePendingFuncPtr;
- mFramePending = pastPresentFence != FenceTime::NO_FENCE &&
- isFencePending(pastPresentFence, graceTimeForPresentFenceMs);
+ mFramePending = fence.fenceTime != FenceTime::NO_FENCE &&
+ isFencePending(fence.fenceTime, graceTimeForPresentFenceMs);
// A frame is missed if the prior frame is still pending. If no longer pending, then we still
// count the frame as missed if the predicted present time was further in the past than when the
@@ -117,9 +149,10 @@
// than a typical frame duration, but should not be so small that it reports reasonable drift as
// a missed frame.
mFrameMissed = mFramePending || [&] {
- const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
+ const nsecs_t pastPresentTime = fence.fenceTime->getSignalTime();
if (pastPresentTime < 0) return false;
- mLastSignaledFrameTime = TimePoint::fromNs(pastPresentTime);
+ mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
+ .expectedPresentTime = fence.expectedPresentTime};
const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
}();
@@ -130,11 +163,14 @@
if (mFrameMissed) mFrameMissedCount++;
if (mHwcFrameMissed) mHwcFrameMissedCount++;
if (mGpuFrameMissed) mGpuFrameMissedCount++;
+
+ mWouldBackpressureHwc = mFramePending && wouldBackpressure;
}
-std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod,
+std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period vsyncPeriod,
+ Period minFramePeriod,
Duration hwcMinWorkDuration) {
- if (wouldPresentEarly(minFramePeriod)) {
+ if (wouldPresentEarly(vsyncPeriod, minFramePeriod)) {
return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
}
return {};
@@ -153,10 +189,8 @@
if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime);
} else {
- for (size_t i = mPresentFences.size()-1; i >= 1; i--) {
- mPresentFences[i] = mPresentFences[i-1];
- }
- mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
+ mPresentFencesLegacy[1] = mPresentFencesLegacy[0];
+ mPresentFencesLegacy[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
}
return presentFenceTime;
}
@@ -169,7 +203,7 @@
}
bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const status_t status = fence->wait(graceTimeMs);
// This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus,
diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
index eeb9c60..20c58eb 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -24,10 +24,10 @@
#include <sys/timerfd.h>
#include <sys/unistd.h>
+#include <common/trace.h>
#include <ftl/concat.h>
#include <ftl/enum.h>
#include <log/log.h>
-#include <utils/Trace.h>
#include <scheduler/Timer.h>
@@ -188,9 +188,9 @@
int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
setDebugState(DebugState::Running);
- if (ATRACE_ENABLED()) {
+ if (SFTRACE_ENABLED()) {
ftl::Concat trace("TimerIteration #", iteration++);
- ATRACE_NAME(trace.c_str());
+ SFTRACE_NAME(trace.c_str());
}
if (nfds == -1) {
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 5448eec..6f4e1f1 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -53,8 +53,13 @@
const auto& target() const { return mTargeter.target(); }
- bool wouldPresentEarly(Period minFramePeriod) const {
- return target().wouldPresentEarly(minFramePeriod);
+ bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const {
+ return target().wouldPresentEarly(vsyncPeriod, minFramePeriod);
+ }
+
+ std::pair<bool /*wouldBackpressure*/, FrameTarget::PresentFence> expectedSignaledPresentFence(
+ Period vsyncPeriod, Period minFramePeriod) const {
+ return target().expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
}
struct Frame {
@@ -169,7 +174,6 @@
}
TEST_F(FrameTargeterTest, recallsPastVsync) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{111};
TimePoint frameBeginTime(1000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -177,16 +181,72 @@
constexpr Duration kFrameDuration = 13ms;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
+ FenceTimePtr fence;
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
+ kRefreshRate);
+ fence = frame.end();
+ }
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
- EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence);
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ ASSERT_TRUE(wouldBackpressure);
+ EXPECT_EQ(presentFence.fenceTime, fence);
+ }
+}
+
+TEST_F(FrameTargeterTest, wouldBackpressureAfterTime) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
+ }
+ {
+ frameBeginTime += kPeriod;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_FALSE(wouldBackpressure);
+ }
+}
+
+TEST_F(FrameTargeterTest, wouldBackpressureAfterTimeLegacy) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
+ }
+ {
+ frameBeginTime += kPeriod;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
}
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -194,101 +254,66 @@
constexpr Duration kFrameDuration = 10ms;
FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr currentFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
-
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
- EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
-
- previousFence = fence;
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, previousFence);
+ previousFence = currentFence;
+ currentFence = frame.end();
}
}
-TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) {
+TEST_F(FrameTargeterTest, recallsPastVsyncFiveVsyncsAhead) {
SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- constexpr Duration kFrameDuration = 10ms;
+ constexpr Duration kFrameDuration = 40ms;
- FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr firstFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
const auto fence = frame.end();
-
- const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
- EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
- EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
-
- frameBeginTime += kPeriod;
- previousFence = fence;
+ if (firstFence == FenceTime::NO_FENCE) {
+ firstFence = fence;
+ }
}
+
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, firstFence);
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
- constexpr Fps kPeakRefreshRate = 240_Hz;
+ constexpr Fps kVsyncRate = 240_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Period kVsyncPeriod = kVsyncRate.getPeriod();
constexpr Duration kFrameDuration = 10ms;
FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr currentFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
- kPeakRefreshRate);
- const auto fence = frame.end();
-
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
- EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
-
- previousFence = fence;
- }
-}
-
-TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAheadVrr) {
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
-
- VsyncId vsyncId{222};
- TimePoint frameBeginTime(2000ms);
- constexpr Fps kRefreshRate = 120_Hz;
- constexpr Fps kPeakRefreshRate = 240_Hz;
- constexpr Period kPeriod = kRefreshRate.getPeriod();
- constexpr Duration kFrameDuration = 10ms;
-
- FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
- for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
- kPeakRefreshRate);
- const auto fence = frame.end();
-
- const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
- EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
- EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
-
- frameBeginTime += kPeriod;
- previousFence = fence;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ EXPECT_EQ(expectedSignaledPresentFence(kVsyncPeriod, kPeriod).second.fenceTime,
+ previousFence);
+ previousFence = currentFence;
+ currentFence = frame.end();
}
}
TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
constexpr Period kPeriod = (60_Hz).getPeriod();
- EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, FenceTime::NO_FENCE);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
}
TEST_F(FrameTargeterTest, detectsEarlyPresent) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -296,20 +321,57 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// `finalFrame` would present early, so it has an earliest present time.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentAfterLongPeriod) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
+ }
+
+ // The target is early if the past present fence was signaled.
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
+
+ frameBeginTime += 10 * kPeriod;
+
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, so it has an earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_NE(std::nullopt, target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
@@ -318,7 +380,6 @@
// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
// when there is expected present time support.
TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -326,26 +387,30 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// `finalFrame` would present early, but we have expected present time support, so it has no
// earliest present time.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_EQ(std::nullopt, target().earliestPresentTime());
}
TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{444};
TimePoint frameBeginTime(4000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -353,17 +418,21 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
// The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
{ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
@@ -371,66 +440,21 @@
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// The target is early if the past present fence was signaled.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_NE(std::nullopt, target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
-TEST_F(FrameTargeterTest, detectsEarlyPresentNVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
- VsyncId vsyncId{444};
- TimePoint frameBeginTime(4000ms);
- Fps refreshRate = 120_Hz;
- Period period = refreshRate.getPeriod();
-
- // The target is not early while past present fences are pending.
- for (int n = 5; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- EXPECT_FALSE(wouldPresentEarly(period));
- EXPECT_FALSE(target().earliestPresentTime());
- }
-
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- auto fence = frame.end();
- frameBeginTime += period;
- fence->signalForTest(frameBeginTime.ns());
-
- // The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(wouldPresentEarly(period));
- EXPECT_FALSE(target().earliestPresentTime());
-
- { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); }
-
- Frame oneEarlyPresentFrame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- // The target is early if the past present fence was signaled.
- EXPECT_TRUE(wouldPresentEarly(period));
- ASSERT_NE(std::nullopt, target().earliestPresentTime());
- EXPECT_EQ(*target().earliestPresentTime(),
- target().expectedPresentTime() - period - kHwcMinWorkDuration);
-
- fence = oneEarlyPresentFrame.end();
- frameBeginTime += period;
- fence->signalForTest(frameBeginTime.ns());
-
- // Change rate to track frame more than 2 vsyncs ahead
- refreshRate = 144_Hz;
- period = refreshRate.getPeriod();
- Frame onePresentEarlyFrame(this, vsyncId++, frameBeginTime, 16ms, refreshRate, refreshRate);
- // The target is not early as last frame as the past frame is tracked for pending.
- EXPECT_FALSE(wouldPresentEarly(period));
-}
-
TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
TimePoint frameBeginTime(5000ms);
constexpr Fps kRefreshRate = 144_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
+ { const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); }
// The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_TRUE(target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 8bb72b8..41a9a1b 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -93,6 +93,12 @@
if (mEnableLocalTonemapping) {
clientCompositionDisplay.tonemapStrategy =
renderengine::DisplaySettings::TonemapStrategy::Local;
+ if (static_cast<ui::PixelFormat>(buffer->getPixelFormat()) == ui::PixelFormat::RGBA_FP16) {
+ clientCompositionDisplay.targetHdrSdrRatio =
+ getState().displayBrightnessNits / getState().sdrWhitePointNits;
+ } else {
+ clientCompositionDisplay.targetHdrSdrRatio = 1.f;
+ }
}
return clientCompositionDisplay;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8fa7e3a..6cce334 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -40,8 +40,10 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
@@ -63,7 +65,7 @@
#include <ftl/fake_guard.h>
#include <ftl/future.h>
#include <ftl/unit.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
@@ -71,9 +73,8 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
-#include <layerproto/LayerProtoParser.h>
+#include <layerproto/LayerProtoHeader.h>
#include <linux/sched/types.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -143,6 +144,7 @@
#include "FrontEnd/LayerLog.h"
#include "FrontEnd/LayerSnapshot.h"
#include "HdrLayerInfoReporter.h"
+#include "Jank/JankTracker.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
#include "LayerRenderArea.h"
@@ -204,8 +206,6 @@
using ui::DisplayPrimaries;
using ui::RenderIntent;
-using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
-
namespace hal = android::hardware::graphics::composer::hal;
namespace {
@@ -373,8 +373,6 @@
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
-const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
-
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
@@ -433,7 +431,7 @@
}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGI("SurfaceFlinger is starting");
hasSyncFramework = running_without_sync_framework(true);
@@ -533,13 +531,7 @@
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
- mLayerLifecycleManagerEnabled =
- base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
-
// These are set by the HWC implementation to indicate that they will use the workarounds.
- mIsHotplugErrViaNegVsync =
- base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
-
mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
@@ -728,6 +720,7 @@
mBootFinished = true;
FlagManager::getMutableInstance().markBootCompleted();
+ ::tracing_perfetto::registerWithPerfetto();
mInitBootPropsFuture.wait();
mRenderEnginePrimeCacheFuture.wait();
@@ -854,13 +847,15 @@
auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, "");
if (algorithm == "gaussian") {
return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
+ } else if (algorithm == "kawase2") {
+ return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
} else {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
}
}
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
addTransactionReadyFilters();
@@ -912,9 +907,11 @@
LOG_ALWAYS_FATAL_IF(!configureLocked(),
"Initial display configuration failed: HWC did not hotplug");
+ mActiveDisplayId = getPrimaryDisplayIdLocked();
+
// Commit primary display.
sp<const DisplayDevice> display;
- if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
+ if (const auto indexOpt = mCurrentState.getDisplayIndex(mActiveDisplayId)) {
const auto& displays = mCurrentState.displays;
const auto& token = displays.keyAt(*indexOpt);
@@ -1006,6 +1003,8 @@
// which we maintain for backwards compatibility.
config.cacheUltraHDR =
base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
+ config.cacheEdgeExtension =
+ base::GetBoolProperty("debug.sf.edge_extension_shader"s, true);
return getRenderEngine().primeCache(config);
});
@@ -1280,20 +1279,14 @@
return BAD_VALUE;
}
+ // TODO: b/277364366 - Require a display token from clients and remove fallback to pacesetter.
std::optional<PhysicalDisplayId> displayIdOpt;
- {
+ if (displayToken) {
Mutex::Autolock lock(mStateLock);
- if (displayToken) {
- displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
- if (!displayIdOpt) {
- ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get());
- return NAME_NOT_FOUND;
- }
- } else {
- // TODO (b/277364366): Clients should be updated to pass in the display they
- // want, rather than us picking an arbitrary one (the active display, in this
- // case).
- displayIdOpt = mActiveDisplayId;
+ displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayIdOpt) {
+ ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get());
+ return NAME_NOT_FOUND;
}
}
@@ -1312,7 +1305,7 @@
const auto mode = desiredMode.mode;
const auto displayId = mode.modePtr->getPhysicalDisplayId();
- ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
+ SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const bool emitEvent = desiredMode.emitEvent;
@@ -1340,22 +1333,16 @@
// VsyncController model is locked.
mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
- if (displayId == mActiveDisplayId) {
- mScheduler->updatePhaseConfiguration(mode.fps);
- }
-
+ mScheduler->updatePhaseConfiguration(displayId, mode.fps);
mScheduler->setModeChangePending(true);
break;
}
case DesiredModeAction::InitiateRenderRateSwitch:
mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false);
-
- if (displayId == mActiveDisplayId) {
- mScheduler->updatePhaseConfiguration(mode.fps);
- }
+ mScheduler->updatePhaseConfiguration(displayId, mode.fps);
if (emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, mode);
+ mScheduler->onDisplayModeChanged(displayId, mode);
}
break;
case DesiredModeAction::None:
@@ -1365,7 +1352,7 @@
status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId, Fps minFps, Fps maxFps) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!displayToken) {
return BAD_VALUE;
@@ -1417,7 +1404,7 @@
// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional.
[[clang::no_thread_safety_analysis]]
void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
- ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
+ SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId);
if (!pendingModeOpt) {
@@ -1447,12 +1434,10 @@
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
activeMode.modePtr->getVsyncRate(), activeMode.fps);
- if (displayId == mActiveDisplayId) {
- mScheduler->updatePhaseConfiguration(activeMode.fps);
- }
+ mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
if (pendingModeOpt->emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, activeMode);
+ mScheduler->onDisplayModeChanged(displayId, activeMode);
}
}
@@ -1473,17 +1458,13 @@
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
- mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true);
- if (displayId == mActiveDisplayId) {
- mScheduler->updatePhaseConfiguration(renderFps);
- }
+ mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true);
+ mScheduler->updatePhaseConfiguration(displayId, renderFps);
}
void SurfaceFlinger::initiateDisplayModeChanges() {
- ATRACE_CALL();
-
- std::optional<PhysicalDisplayId> displayToUpdateImmediately;
+ SFTRACE_CALL();
for (const auto& [displayId, physical] : mPhysicalDisplays) {
auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
@@ -1538,21 +1519,14 @@
if (outTimeline.refreshRequired) {
scheduleComposite(FrameHint::kNone);
} else {
- // TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange`
- // for all displays. This was only needed when the loop iterated over `mDisplays` rather
- // than `mPhysicalDisplays`.
- displayToUpdateImmediately = displayId;
- }
- }
+ // HWC has requested to apply the mode change immediately rather than on the next frame.
+ finalizeDisplayModeChange(displayId);
- if (displayToUpdateImmediately) {
- const auto displayId = *displayToUpdateImmediately;
- finalizeDisplayModeChange(displayId);
-
- const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
- if (desiredModeOpt &&
- mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
- applyActiveMode(displayId);
+ const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
+ if (desiredModeOpt &&
+ mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ applyActiveMode(displayId);
+ }
}
}
}
@@ -1560,7 +1534,7 @@
void SurfaceFlinger::disableExpensiveRendering() {
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
- ATRACE_NAME(whence);
+ SFTRACE_NAME(whence);
if (mPowerAdvisor->isUsingExpensiveRendering()) {
for (const auto& [_, display] : mDisplays) {
constexpr bool kDisable = false;
@@ -2171,12 +2145,12 @@
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
-void SurfaceFlinger::scheduleCommit(FrameHint hint) {
+void SurfaceFlinger::scheduleCommit(FrameHint hint, Duration workDurationSlack) {
if (hint == FrameHint::kActive) {
mScheduler->resetIdleTimer();
}
mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
- mScheduler->scheduleFrame();
+ mScheduler->scheduleFrame(workDurationSlack);
}
void SurfaceFlinger::scheduleComposite(FrameHint hint) {
@@ -2193,41 +2167,25 @@
static_cast<void>(mScheduler->schedule([this] { sample(); }));
}
-nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
- if (const auto display = getDefaultDisplayDeviceLocked()) {
- return display->getVsyncPeriodFromHWC();
- }
-
- return 0;
-}
-
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
vsyncPeriod.has_value()) {
- // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
- if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) {
- const auto errorCode = static_cast<int32_t>(-timestamp);
- ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
- mScheduler->dispatchHotplugError(errorCode);
- return;
- }
-
if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
const int32_t value = static_cast<int32_t>(-timestamp);
// one byte is good enough to encode android.hardware.drm.HdcpLevel
const int32_t maxLevel = (value >> 8) & 0xFF;
const int32_t connectedLevel = value & 0xFF;
- ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__,
- connectedLevel, maxLevel, hwcDisplayId);
+ ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64,
+ __func__, connectedLevel, maxLevel, hwcDisplayId);
updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
return;
}
}
- ATRACE_NAME(vsyncPeriod
- ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
- : ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
+ SFTRACE_NAME(vsyncPeriod
+ ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
+ : ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
Mutex::Autolock lock(mStateLock);
if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) {
@@ -2259,7 +2217,7 @@
if (FlagManager::getInstance().hotplug2()) {
// TODO(b/311403559): use enum type instead of int
const auto errorCode = static_cast<int32_t>(event);
- ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
+ ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
mScheduler->dispatchHotplugError(errorCode);
}
}
@@ -2285,12 +2243,12 @@
}
void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) {
- ATRACE_CALL();
+ SFTRACE_CALL();
mScheduler->forceNextResync();
}
void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
- ATRACE_CALL();
+ SFTRACE_CALL();
const char* const whence = __func__;
static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
kMainThreadContext) {
@@ -2299,7 +2257,7 @@
const Fps refreshRate = Fps::fromPeriodNsecs(
getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos
: data.vsyncPeriodNanos);
- ATRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue());
+ SFTRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue());
const auto renderRate = mDisplayModeController.getActiveMode(*displayIdOpt).fps;
constexpr bool kSetByHwc = true;
@@ -2309,6 +2267,18 @@
}));
}
+void SurfaceFlinger::onComposerHalHdcpLevelsChanged(hal::HWDisplayId hwcDisplayId,
+ const HdcpLevels& levels) {
+ if (FlagManager::getInstance().hdcp_level_hal()) {
+ // TODO(b/362270040): propagate enum constants
+ const int32_t maxLevel = static_cast<int32_t>(levels.maxLevel);
+ const int32_t connectedLevel = static_cast<int32_t>(levels.connectedLevel);
+ ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, __func__,
+ connectedLevel, maxLevel, hwcDisplayId);
+ updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
+ }
+}
+
void SurfaceFlinger::configure() {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
@@ -2316,37 +2286,6 @@
}
}
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs,
- bool flushTransactions,
- bool& outTransactionsAreEmpty) {
- ATRACE_CALL();
- frontend::Update update;
- if (flushTransactions) {
- update = flushLifecycleUpdates();
- if (mTransactionTracing) {
- mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
- update, mFrontEndDisplayInfos,
- mFrontEndDisplayInfosChanged);
- }
- }
-
- bool needsTraversal = false;
- if (flushTransactions) {
- needsTraversal |= commitMirrorDisplays(vsyncId);
- needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
- needsTraversal |= applyTransactions(update.transactions);
- }
- outTransactionsAreEmpty = !needsTraversal;
- const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
- if (shouldCommit) {
- commitTransactionsLegacy();
- }
-
- bool mustComposite = latchBuffers() || shouldCommit;
- updateLayerGeometry();
- return mustComposite;
-}
-
void SurfaceFlinger::updateLayerHistory(nsecs_t now) {
for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
using Changes = frontend::RequestedLayerState::Changes;
@@ -2418,10 +2357,10 @@
bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
bool flushTransactions, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
- ATRACE_CALL();
+ SFTRACE_CALL();
frontend::Update update;
if (flushTransactions) {
- ATRACE_NAME("TransactionHandler:flushTransactions");
+ SFTRACE_NAME("TransactionHandler:flushTransactions");
// Locking:
// 1. to prevent onHandleDestroyed from being called while the state lock is held,
// we must keep a copy of the transactions (specifically the composer
@@ -2435,7 +2374,7 @@
{
// TODO(b/238781169) lockless queue this and keep order.
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- update.layerCreatedStates = std::move(mCreatedLayers);
+ update.legacyLayers = std::move(mCreatedLayers);
mCreatedLayers.clear();
update.newLayers = std::move(mNewLayers);
mNewLayers.clear();
@@ -2454,11 +2393,8 @@
}
mLayerLifecycleManager.applyTransactions(update.transactions);
mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
- for (auto& legacyLayer : update.layerCreatedStates) {
- sp<Layer> layer = legacyLayer.layer.promote();
- if (layer) {
- mLegacyLayers[layer->sequence] = layer;
- }
+ for (auto& legacyLayer : update.legacyLayers) {
+ mLegacyLayers[legacyLayer->sequence] = legacyLayer;
}
mLayerHierarchyBuilder.update(mLayerLifecycleManager);
}
@@ -2473,7 +2409,7 @@
mustComposite |= applyAndCommitDisplayTransactionStatesLocked(update.transactions);
{
- ATRACE_NAME("LayerSnapshotBuilder:update");
+ SFTRACE_NAME("LayerSnapshotBuilder:update");
frontend::LayerSnapshotBuilder::Args
args{.root = mLayerHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLayerLifecycleManager,
@@ -2514,7 +2450,7 @@
}
bool newDataLatched = false;
- ATRACE_NAME("DisplayCallbackAndStatsUpdates");
+ SFTRACE_NAME("DisplayCallbackAndStatsUpdates");
mustComposite |= applyTransactionsLocked(update.transactions);
traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
const nsecs_t latchTime = systemTime();
@@ -2562,17 +2498,25 @@
it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
newDataLatched = true;
- mLayersWithQueuedFrames.emplace(it->second);
+ frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence);
+ gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported;
+ mLayersWithQueuedFrames.emplace(it->second, gameMode);
mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
updateLayerHistory(latchTime);
- mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == mLayersIdsWithQueuedFrames.end())
- return;
- Region visibleReg;
- visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
- invalidateLayerStack(snapshot.outputFilter, visibleReg);
+ mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ // update output dirty region if we have a queued buffer that is visible or a snapshot
+ // recently became invisible
+ // TODO(b/360050020) investigate if we need to update dirty region when layer color changes
+ if ((snapshot.isVisible &&
+ (mLayersIdsWithQueuedFrames.find(snapshot.path.id) !=
+ mLayersIdsWithQueuedFrames.end())) ||
+ (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) {
+ Region visibleReg;
+ visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
+ invalidateLayerStack(snapshot.outputFilter, visibleReg);
+ }
});
for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
@@ -2580,7 +2524,7 @@
}
{
- ATRACE_NAME("LayerLifecycleManager:commitChanges");
+ SFTRACE_NAME("LayerLifecycleManager:commitChanges");
mLayerLifecycleManager.commitChanges();
}
@@ -2603,7 +2547,7 @@
const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
- ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
+ SFTRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
if (pacesetterFrameTarget.didMissFrame()) {
mTimeStats->incrementMissedFrames();
@@ -2630,13 +2574,16 @@
}
}
- if (pacesetterFrameTarget.isFramePending()) {
+ if (pacesetterFrameTarget.wouldBackpressureHwc()) {
if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
if (FlagManager::getInstance().vrr_config()) {
mScheduler->getVsyncSchedule()->getTracker().onFrameMissed(
pacesetterFrameTarget.expectedPresentTime());
}
- scheduleCommit(FrameHint::kNone);
+ const Duration slack = FlagManager::getInstance().allow_n_vsyncs_in_targeter()
+ ? TimePoint::now() - pacesetterFrameTarget.frameBeginTime()
+ : Duration::fromNs(0);
+ scheduleCommit(FrameHint::kNone, slack);
return false;
}
}
@@ -2683,11 +2630,8 @@
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
bool transactionsAreEmpty = false;
- if (mLayerLifecycleManagerEnabled) {
- mustComposite |=
- updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
- flushTransactions, transactionsAreEmpty);
- }
+ mustComposite |= updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+ flushTransactions, transactionsAreEmpty);
// Tell VsyncTracker that we are going to present this frame before scheduling
// setTransactionFlags which will schedule another SF frame. This was if the tracker
@@ -2721,9 +2665,7 @@
mUpdateAttachedChoreographer = false;
Mutex::Autolock lock(mStateLock);
- mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled
- ? &mLayerHierarchyBuilder.getHierarchy()
- : nullptr,
+ mScheduler->chooseRefreshRateForContent(&mLayerHierarchyBuilder.getHierarchy(),
updateAttachedChoreographer);
if (FlagManager::getInstance().connected_display()) {
@@ -2756,7 +2698,7 @@
frameTargeters.get(pacesetterId)->get()->target();
const VsyncId vsyncId = pacesetterTarget.vsyncId();
- ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
+ SFTRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
compositionengine::CompositionRefreshArgs refreshArgs;
refreshArgs.powerCallback = this;
@@ -2793,17 +2735,14 @@
const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test(
compositionengine::Feature::kSnapshotLayerMetadata);
- if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) {
- updateLayerMetadataSnapshot();
- mLayerMetadataSnapshotNeeded = false;
- }
refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
if (!FlagManager::getInstance().ce_fence_promise()) {
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto& layer : mLayersWithQueuedFrames) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE())
+ for (auto& [layer, _] : mLayersWithQueuedFrames) {
+ if (const auto& layerFE = layer->getCompositionEngineLayerFE(
+ {static_cast<uint32_t>(layer->sequence)}))
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
}
@@ -2844,7 +2783,7 @@
constexpr bool kCursorOnly = false;
const auto layers = moveSnapshotsToCompositionArgs(refreshArgs, kCursorOnly);
- if (mLayerLifecycleManagerEnabled && !mVisibleRegionsDirty) {
+ if (!mVisibleRegionsDirty) {
for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
auto compositionDisplay = display->getCompositionDisplay();
if (!compositionDisplay->getState().isEnabled) continue;
@@ -2878,8 +2817,9 @@
}
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto& layer : mLayersWithQueuedFrames) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
+ for (auto& [layer, _] : mLayersWithQueuedFrames) {
+ if (const auto& layerFE = layer->getCompositionEngineLayerFE(
+ {static_cast<uint32_t>(layer->sequence)})) {
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
// Some layers are not displayed and do not yet have a future release fence
if (layerFE->getReleaseFencePromiseStatus() ==
@@ -2910,9 +2850,7 @@
for (auto [layer, layerFE] : layers) {
CompositionResult compositionResult{layerFE->stealCompositionResult()};
for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
- Layer* clonedFrom = layer->getClonedFrom().get();
- auto owningLayer = clonedFrom ? clonedFrom : layer;
- owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack);
+ layer->onLayerDisplayed(std::move(releaseFence), layerStack);
}
if (compositionResult.lastClientCompositionFence) {
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
@@ -2920,6 +2858,7 @@
}
}
+ SFTRACE_NAME("postComposition");
mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
// Send a power hint after presentation is finished.
@@ -2927,6 +2866,9 @@
// Now that the current frame has been presented above, PowerAdvisor needs the present time
// of the previous frame (whose fence is signaled by now) to determine how long the HWC had
// waited on that fence to retire before presenting.
+ // TODO(b/355238809) `presentFenceForPreviousFrame` might not always be signaled (e.g. on
+ // devices
+ // where HWC does not block on the previous present fence). Revise this assumtion.
const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
@@ -3014,21 +2956,6 @@
return resultsPerDisplay;
}
-void SurfaceFlinger::updateLayerGeometry() {
- ATRACE_CALL();
-
- if (mVisibleRegionsDirty) {
- computeLayerBounds();
- }
-
- for (auto& layer : mLayersPendingRefresh) {
- Region visibleReg;
- visibleReg.set(layer->getScreenBounds());
- invalidateLayerStack(layer->getOutputFilter(), visibleReg);
- }
- mLayersPendingRefresh.clear();
-}
-
bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const {
// Even though the camera layer may be using an HDR transfer function or otherwise be "HDR"
// the device may need to avoid boosting the brightness as a result of these layers to
@@ -3099,7 +3026,7 @@
void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId,
const scheduler::FrameTargeters& frameTargeters,
nsecs_t presentStartTime) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
@@ -3195,10 +3122,10 @@
}
mLayersWithBuffersRemoved.clear();
- for (const auto& layer: mLayersWithQueuedFrames) {
+ for (const auto& [layer, gameMode] : mLayersWithQueuedFrames) {
layer->onCompositionPresented(pacesetterDisplay.get(),
pacesetterGpuCompositionDoneFenceTime,
- pacesetterPresentFenceTime, compositorTiming);
+ pacesetterPresentFenceTime, compositorTiming, gameMode);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -3259,28 +3186,20 @@
}
};
- if (mLayerLifecycleManagerEnabled) {
- mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&, compositionDisplay = compositionDisplay](
- std::unique_ptr<frontend::LayerSnapshot>&
- snapshot) FTL_FAKE_GUARD(kMainThreadContext) {
- auto it = mLegacyLayers.find(snapshot->sequence);
- LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
- auto& legacyLayer = it->second;
- sp<LayerFE> layerFe =
- legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&, compositionDisplay = compositionDisplay](
+ std::unique_ptr<frontend::LayerSnapshot>& snapshot)
+ FTL_FAKE_GUARD(kMainThreadContext) {
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ auto& legacyLayer = it->second;
+ sp<LayerFE> layerFe =
+ legacyLayer->getCompositionEngineLayerFE(snapshot->path);
- updateInfoFn(compositionDisplay, *snapshot, layerFe);
- });
- } else {
- mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
- const auto layerFe = layer->getCompositionEngineLayerFE();
- const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
- updateInfoFn(compositionDisplay, snapshot, layerFe);
- });
- }
+ updateInfoFn(compositionDisplay, *snapshot, layerFe);
+ });
listener->dispatchHdrLayerInfo(info);
}
}
@@ -3326,9 +3245,8 @@
if (!layer->hasTrustedPresentationListener()) {
return;
}
- const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled
- ? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
- : layer->getLayerSnapshot();
+ const frontend::LayerSnapshot* snapshot =
+ mLayerSnapshotBuilder.getSnapshot(layer->sequence);
std::optional<const DisplayDevice*> displayOpt = std::nullopt;
if (snapshot) {
displayOpt = layerStackToDisplay.get(snapshot->outputFilter.layerStack);
@@ -3340,48 +3258,18 @@
});
}
- // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
+ // Even though SFTRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
- if (ATRACE_ENABLED()) {
+ if (SFTRACE_ENABLED()) {
// getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
- ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
+ SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
}
logFrameStats(presentTime);
}
-FloatRect SurfaceFlinger::getMaxDisplayBounds() {
- const ui::Size maxSize = [this] {
- ftl::FakeGuard guard(mStateLock);
-
- // The LayerTraceGenerator tool runs without displays.
- if (mDisplays.empty()) return ui::Size{5000, 5000};
-
- return std::accumulate(mDisplays.begin(), mDisplays.end(), ui::kEmptySize,
- [](ui::Size size, const auto& pair) -> ui::Size {
- const auto& display = pair.second;
- return {std::max(size.getWidth(), display->getWidth()),
- std::max(size.getHeight(), display->getHeight())};
- });
- }();
-
- // Ignore display bounds for now since they will be computed later. Use a large Rect bound
- // to ensure it's bigger than an actual display will be.
- const float xMax = maxSize.getWidth() * 10.f;
- const float yMax = maxSize.getHeight() * 10.f;
-
- return {-xMax, -yMax, xMax, yMax};
-}
-
-void SurfaceFlinger::computeLayerBounds() {
- const FloatRect maxBounds = getMaxDisplayBounds();
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
- }
-}
-
void SurfaceFlinger::commitTransactions() {
- ATRACE_CALL();
+ SFTRACE_CALL();
mDebugInTransaction = systemTime();
// Here we're guaranteed that some transaction flags are set
@@ -3393,28 +3281,6 @@
mDebugInTransaction = 0;
}
-void SurfaceFlinger::commitTransactionsLegacy() {
- ATRACE_CALL();
-
- // Keep a copy of the drawing state (that is going to be overwritten
- // by commitTransactionsLocked) outside of mStateLock so that the side
- // effects of the State assignment don't happen with mStateLock held,
- // which can cause deadlocks.
- State drawingState(mDrawingState);
-
- Mutex::Autolock lock(mStateLock);
- mDebugInTransaction = systemTime();
-
- // Here we're guaranteed that some transaction flags are set
- // so we can call commitTransactionsLocked unconditionally.
- // We clear the flags with mStateLock held to guarantee that
- // mCurrentState won't change until the transaction is committed.
- mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit);
- commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
-
- mDebugInTransaction = 0;
-}
-
std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes(
PhysicalDisplayId displayId) const {
std::vector<HWComposer::HWCDisplayMode> hwcModes;
@@ -3673,7 +3539,12 @@
state.physical = {.id = displayId,
.hwcDisplayId = hwcDisplayId,
.activeMode = std::move(activeMode)};
- state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
+ if (mIsHdcpViaNegVsync) {
+ state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
+ } else {
+ // TODO(b/349703362): Remove this when HDCP aidl API becomes ready
+ state.isSecure = true; // All physical displays are currently considered secure.
+ }
state.isProtected = true;
state.displayName = std::move(info.name);
@@ -3697,16 +3568,6 @@
mPhysicalDisplays.erase(displayId);
}
-void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
- const scheduler::FrameRateMode& mode) {
- // TODO(b/255635821): Merge code paths and move to Scheduler.
- const auto onDisplayModeChanged = displayId == mActiveDisplayId
- ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
- : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
-
- ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode);
-}
-
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -3782,7 +3643,7 @@
if (const auto& physical = state.physical) {
const auto& mode = *physical->activeMode;
mDisplayModeController.setActiveMode(physical->id, mode.getId(), mode.getVsyncRate(),
- mode.getVsyncRate());
+ mode.getPeakFps());
}
display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
@@ -3852,11 +3713,20 @@
state.surface.get());
const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());
LOG_FATAL_IF(!displayId);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ const auto frameBufferSurface =
+ sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer,
+ state.physical->activeMode->getResolution(),
+ ui::Size(maxGraphicsWidth, maxGraphicsHeight));
+ displaySurface = frameBufferSurface;
+ producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer();
+#else
displaySurface =
sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
state.physical->activeMode->getResolution(),
ui::Size(maxGraphicsWidth, maxGraphicsHeight));
producer = bqProducer;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
LOG_FATAL_IF(!displaySurface);
@@ -3949,7 +3819,8 @@
mDisplays.erase(displayToken);
if (const auto& physical = currentState.physical) {
- getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id);
+ getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id,
+ /*physicalSize=*/std::nullopt);
}
processDisplayAdded(displayToken, currentState);
@@ -3960,11 +3831,8 @@
setPowerModeInternal(display, hal::PowerMode::ON);
}
- // TODO(b/175678251) Call a listener instead.
- if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
- const Fps refreshRate =
- mDisplayModeController.getActiveMode(display->getPhysicalId()).fps;
- mScheduler->resetPhaseConfiguration(refreshRate);
+ if (display->getPhysicalId() == mActiveDisplayId) {
+ onActiveDisplayChangedLocked(nullptr, *display);
}
}
return;
@@ -4048,14 +3916,6 @@
// Commit display transactions.
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
mFrontEndDisplayInfosChanged = displayTransactionNeeded;
- if (displayTransactionNeeded && !mLayerLifecycleManagerEnabled) {
- processDisplayChangesLocked();
- mFrontEndDisplayInfos.clear();
- for (const auto& [_, display] : mDisplays) {
- mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
- }
- }
- mForceTransactionDisplayChange = displayTransactionNeeded;
if (mSomeChildrenChanged) {
mVisibleRegionsDirty = true;
@@ -4063,51 +3923,6 @@
mUpdateInputInfo = true;
}
- // Update transform hint.
- if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
- // Layers and/or displays have changed, so update the transform hint for each layer.
- //
- // NOTE: we do this here, rather than when presenting the display so that
- // the hint is set before we acquire a buffer from the surface texture.
- //
- // NOTE: layer transactions have taken place already, so we use their
- // drawing state. However, SurfaceFlinger's own transaction has not
- // happened yet, so we must use the current state layer list
- // (soon to become the drawing state list).
- //
- sp<const DisplayDevice> hintDisplay;
- ui::LayerStack layerStack;
-
- mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
- // NOTE: we rely on the fact that layers are sorted by
- // layerStack first (so we don't have to traverse the list
- // of displays for every layer).
- if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) {
- layerStack = filter.layerStack;
- hintDisplay = nullptr;
-
- // Find the display that includes the layer.
- for (const auto& [token, display] : mDisplays) {
- if (!display->getCompositionDisplay()->includesLayer(filter)) {
- continue;
- }
-
- // Pick the primary display if another display mirrors the layer.
- if (hintDisplay) {
- hintDisplay = nullptr;
- break;
- }
-
- hintDisplay = display;
- }
- }
-
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
- }
- });
- }
-
if (mLayersAdded) {
mLayersAdded = false;
// Layers have been added.
@@ -4121,14 +3936,6 @@
mLayersRemoved = false;
mVisibleRegionsDirty = true;
mUpdateInputInfo = true;
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- if (mLayersPendingRemoval.indexOf(sp<Layer>::fromExisting(layer)) >= 0) {
- // this layer is not visible anymore
- Region visibleReg;
- visibleReg.set(layer->getScreenBounds());
- invalidateLayerStack(layer->getOutputFilter(), visibleReg);
- }
- });
}
if (transactionFlags & eInputInfoUpdateNeeded) {
@@ -4142,7 +3949,7 @@
if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
std::vector<WindowInfo> windowInfos;
std::vector<DisplayInfo> displayInfos;
@@ -4172,7 +3979,7 @@
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
visibleWindowsChanged, vsyncId, frameTime]() {
- ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
+ SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
mWindowInfosListenerInvoker
->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
@@ -4235,23 +4042,10 @@
outWindowInfos.reserve(sNumWindowInfos);
sNumWindowInfos = 0;
- if (mLayerLifecycleManagerEnabled) {
- mLayerSnapshotBuilder.forEachInputSnapshot(
- [&outWindowInfos](const frontend::LayerSnapshot& snapshot) {
- outWindowInfos.push_back(snapshot.inputInfo);
- });
- } else {
- mDrawingState.traverseInReverseZOrder([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) {
- if (!layer->needsInputInfo()) return;
- const auto opt =
- mFrontEndDisplayInfos.get(layer->getLayerStack())
- .transform([](const frontend::DisplayInfo& info) {
- return Layer::InputDisplayArgs{&info.transform, info.isSecure};
- });
-
- outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
- });
- }
+ mLayerSnapshotBuilder.forEachInputSnapshot(
+ [&outWindowInfos](const frontend::LayerSnapshot& snapshot) {
+ outWindowInfos.push_back(snapshot.inputInfo);
+ });
sNumWindowInfos = outWindowInfos.size();
@@ -4285,7 +4079,7 @@
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
@@ -4310,25 +4104,14 @@
}
}
-void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
- PhysicalDisplayId displayId = [&]() {
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
-}
-
void SurfaceFlinger::notifyCpuLoadUp() {
mPowerAdvisor->notifyCpuLoadUp();
}
void SurfaceFlinger::onChoreographerAttached() {
- ATRACE_CALL();
- if (mLayerLifecycleManagerEnabled) {
- mUpdateAttachedChoreographer = true;
- scheduleCommit(FrameHint::kNone);
- }
+ SFTRACE_CALL();
+ mUpdateAttachedChoreographer = true;
+ scheduleCommit(FrameHint::kNone);
}
void SurfaceFlinger::onExpectedPresentTimePosted(TimePoint expectedPresentTime,
@@ -4453,6 +4236,8 @@
if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx,
NotifyExpectedPresentHintStatus::Sent)) {
sendHint();
+ constexpr bool kAllowToEnable = true;
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable);
}
}));
}
@@ -4552,75 +4337,9 @@
}
void SurfaceFlinger::doCommitTransactions() {
- ATRACE_CALL();
-
- if (!mLayersPendingRemoval.isEmpty()) {
- // Notify removed layers now that they can't be drawn from
- for (const auto& l : mLayersPendingRemoval) {
- // Ensure any buffers set to display on any children are released.
- if (l->isRemovedFromCurrentState()) {
- l->latchAndReleaseBuffer();
- }
-
- // If a layer has a parent, we allow it to out-live it's handle
- // with the idea that the parent holds a reference and will eventually
- // be cleaned up. However no one cleans up the top-level so we do so
- // here.
- if (l->isAtRoot()) {
- l->setIsAtRoot(false);
- mCurrentState.layersSortedByZ.remove(l);
- }
-
- // If the layer has been removed and has no parent, then it will not be reachable
- // when traversing layers on screen. Add the layer to the offscreenLayers set to
- // ensure we can copy its current to drawing state.
- if (!l->getParent()) {
- mOffscreenLayers.emplace(l.get());
- }
- }
- mLayersPendingRemoval.clear();
- }
-
+ SFTRACE_CALL();
mDrawingState = mCurrentState;
mCurrentState.colorMatrixChanged = false;
-
- if (mVisibleRegionsDirty) {
- for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
- rootLayer->commitChildList();
- }
- }
-
- commitOffscreenLayers();
- if (mLayerMirrorRoots.size() > 0) {
- std::deque<Layer*> pendingUpdates;
- pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(),
- mLayerMirrorRoots.end());
- std::vector<Layer*> needsUpdating;
- for (Layer* cloneRoot : mLayerMirrorRoots) {
- pendingUpdates.pop_front();
- if (cloneRoot->isRemovedFromCurrentState()) {
- continue;
- }
- if (cloneRoot->updateMirrorInfo(pendingUpdates)) {
- } else {
- needsUpdating.push_back(cloneRoot);
- }
- }
- for (Layer* cloneRoot : needsUpdating) {
- cloneRoot->updateMirrorInfo({});
- }
- }
-}
-
-void SurfaceFlinger::commitOffscreenLayers() {
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
- if (layer->clearTransactionFlags(eTransactionNeeded)) {
- layer->doTransaction(0);
- layer->commitChildList();
- }
- });
- }
}
void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty) {
@@ -4632,160 +4351,30 @@
}
}
-bool SurfaceFlinger::latchBuffers() {
- ATRACE_CALL();
-
- const nsecs_t latchTime = systemTime();
-
- bool visibleRegions = false;
- bool frameQueued = false;
- bool newDataLatched = false;
-
- // Store the set of layers that need updates. This set must not change as
- // buffers are being latched, as this could result in a deadlock.
- // Example: Two producers share the same command stream and:
- // 1.) Layer 0 is latched
- // 2.) Layer 0 gets a new frame
- // 2.) Layer 1 gets a new frame
- // 3.) Layer 1 is latched.
- // Display is now waiting on Layer 1's frame, which is behind layer 0's
- // second frame. But layer 0's second frame could be waiting on display.
- mDrawingState.traverse([&](Layer* layer) {
- if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
- const uint32_t flags = layer->doTransaction(0);
- if (flags & Layer::eVisibleRegion) {
- mVisibleRegionsDirty = true;
- }
- }
-
- if (layer->hasReadyFrame() || layer->willReleaseBufferOnLatch()) {
- frameQueued = true;
- mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
- } else {
- layer->useEmptyDamage();
- if (!layer->hasBuffer()) {
- // The last latch time is used to classify a missed frame as buffer stuffing
- // instead of a missed frame. This is used to identify scenarios where we
- // could not latch a buffer or apply a transaction due to backpressure.
- // We only update the latch time for buffer less layers here, the latch time
- // is updated for buffer layers when the buffer is latched.
- layer->updateLastLatchTime(latchTime);
- }
- }
- });
- mForceTransactionDisplayChange = false;
-
- // The client can continue submitting buffers for offscreen layers, but they will not
- // be shown on screen. Therefore, we need to latch and release buffers of offscreen
- // layers to ensure dequeueBuffer doesn't block indefinitely.
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* l) { l->latchAndReleaseBuffer(); });
- }
-
- if (!mLayersWithQueuedFrames.empty()) {
- // mStateLock is needed for latchBuffer as LayerRejecter::reject()
- // writes to Layer current state. See also b/119481871
- Mutex::Autolock lock(mStateLock);
-
- for (const auto& layer : mLayersWithQueuedFrames) {
- if (layer->willReleaseBufferOnLatch()) {
- mLayersWithBuffersRemoved.emplace(layer);
- }
- if (layer->latchBuffer(visibleRegions, latchTime)) {
- mLayersPendingRefresh.push_back(layer);
- newDataLatched = true;
- }
- layer->useSurfaceDamage();
- }
- }
-
- mVisibleRegionsDirty |= visibleRegions;
-
- // If we will need to wake up at some time in the future to deal with a
- // queued frame that shouldn't be displayed during this vsync period, wake
- // up during the next vsync period to check again.
- if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) {
- scheduleCommit(FrameHint::kNone);
- }
-
- // enter boot animation on first buffer latch
- if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
- ALOGI("Enter boot animation");
- mBootStage = BootStage::BOOTANIMATION;
- }
-
- if (mLayerMirrorRoots.size() > 0) {
- mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
- }
-
- // Only continue with the refresh if there is actually new work to do
- return !mLayersWithQueuedFrames.empty() && newDataLatched;
-}
-
status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parent,
uint32_t* outTransformHint) {
if (mNumLayers >= MAX_LAYERS) {
+ static std::atomic<nsecs_t> lasttime{0};
+ nsecs_t now = systemTime();
+ if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) {
+ ALOGE("AddClientLayer already dumped 10s before");
+ return NO_MEMORY;
+ } else {
+ lasttime = now;
+ }
+
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
MAX_LAYERS);
- static_cast<void>(mScheduler->schedule([=, this] {
- ALOGE("Dumping layer keeping > 20 children alive:");
- bool leakingParentLayerFound = false;
- mDrawingState.traverse([&](Layer* layer) {
- if (leakingParentLayerFound) {
- return;
- }
- if (layer->getChildrenCount() > 20) {
- leakingParentLayerFound = true;
- sp<Layer> parent = sp<Layer>::fromExisting(layer);
- while (parent) {
- ALOGE("Parent Layer: %s%s", parent->getName().c_str(),
- (parent->isHandleAlive() ? "handleAlive" : ""));
- parent = parent->getParent();
- }
- // Sample up to 100 layers
- ALOGE("Dumping random sampling of child layers total(%zu): ",
- layer->getChildrenCount());
- int sampleSize = (layer->getChildrenCount() / 100) + 1;
- layer->traverseChildren([&](Layer* layer) {
- if (rand() % sampleSize == 0) {
- ALOGE("Child Layer: %s%s", layer->getName().c_str(),
- (layer->isHandleAlive() ? "handleAlive" : ""));
- }
- });
- }
- });
-
- int numLayers = 0;
- mDrawingState.traverse([&](Layer* layer) { numLayers++; });
-
- ALOGE("Dumping random sampling of on-screen layers total(%u):", numLayers);
- mDrawingState.traverse([&](Layer* layer) {
- // Aim to dump about 200 layers to avoid totally trashing
- // logcat. On the other hand, if there really are 4096 layers
- // something has gone totally wrong its probably the most
- // useful information in logcat.
- if (rand() % 20 == 13) {
- ALOGE("Layer: %s%s", layer->getName().c_str(),
- (layer->isHandleAlive() ? "handleAlive" : ""));
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- }
- });
- ALOGE("Dumping random sampling of off-screen layers total(%zu): ",
- mOffscreenLayers.size());
- for (Layer* offscreenLayer : mOffscreenLayers) {
- if (rand() % 20 == 13) {
- ALOGE("Offscreen-layer: %s%s", offscreenLayer->getName().c_str(),
- (offscreenLayer->isHandleAlive() ? "handleAlive" : ""));
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- }
- }
+ static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ ALOGE("Dumping on-screen layers.");
+ mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy());
+ ALOGE("Dumping off-screen layers.");
+ mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy());
}));
return NO_MEMORY;
}
- layer->updateTransformHint(mActiveDisplayTransformHint);
if (outTransformHint) {
*outTransformHint = mActiveDisplayTransformHint;
}
@@ -4793,7 +4382,7 @@
args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
{
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
+ mCreatedLayers.emplace_back(layer);
mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
args.mirrorLayerHandle.clear();
args.parentHandle.clear();
@@ -4810,7 +4399,7 @@
uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) {
uint32_t transactionFlags = mTransactionFlags.fetch_and(~mask);
- ATRACE_INT("mTransactionFlags", transactionFlags);
+ SFTRACE_INT("mTransactionFlags", transactionFlags);
return transactionFlags & mask;
}
@@ -4818,7 +4407,7 @@
const sp<IBinder>& applyToken, FrameHint frameHint) {
mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken);
uint32_t transactionFlags = mTransactionFlags.fetch_or(mask);
- ATRACE_INT("mTransactionFlags", transactionFlags);
+ SFTRACE_INT("mTransactionFlags", transactionFlags);
if (const bool scheduled = transactionFlags & mask; !scheduled) {
scheduleCommit(frameHint);
@@ -4843,8 +4432,8 @@
// for stability reasons.
if (!transaction.isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + 1s) {
- ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
- desiredPresentTime, expectedPresentTime);
+ SFTRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
+ desiredPresentTime, expectedPresentTime);
return TransactionReadiness::NotReady;
}
@@ -4856,117 +4445,22 @@
// incorrectly as the frame rate of SF changed before it drained the older transactions.
if (ftl::to_underlying(vsyncId) == FrameTimelineInfo::INVALID_VSYNC_ID &&
!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
- ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime,
- transaction.originUid);
+ SFTRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
+ expectedPresentTime, transaction.originUid);
return TransactionReadiness::NotReady;
}
// If the client didn't specify desiredPresentTime, use the vsyncId to determine the
// expected present time of this transaction.
if (transaction.isAutoTimestamp && frameIsEarly(expectedPresentTime, vsyncId)) {
- ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
- transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
+ SFTRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
+ transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
return TransactionReadiness::NotReady;
}
return TransactionReadiness::Ready;
}
-TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheckLegacy(
- const TransactionHandler::TransactionFlushState& flushState) {
- using TransactionReadiness = TransactionHandler::TransactionReadiness;
- auto ready = TransactionReadiness::Ready;
- flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
- resolvedState) -> bool {
- sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
-
- const auto& transaction = *flushState.transaction;
- const auto& s = resolvedState.state;
- // check for barrier frames
- if (s.bufferData->hasBarrier) {
- // The current producerId is already a newer producer than the buffer that has a
- // barrier. This means the incoming buffer is older and we can release it here. We
- // don't wait on the barrier since we know that's stale information.
- if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) {
- layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
- resolvedState.externalTexture->getBuffer(),
- s.bufferData->frameNumber,
- s.bufferData->acquireFence);
- // Delete the entire state at this point and not just release the buffer because
- // everything associated with the Layer in this Transaction is now out of date.
- ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
- layer->getDebugName(), layer->getDrawingState().barrierProducerId,
- s.bufferData->producerId);
- return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
- }
-
- if (layer->getDrawingState().barrierFrameNumber < s.bufferData->barrierFrameNumber) {
- const bool willApplyBarrierFrame =
- flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
- ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
- s.bufferData->barrierFrameNumber));
- if (!willApplyBarrierFrame) {
- ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
- layer->getDebugName(),
- layer->getDrawingState().barrierFrameNumber,
- s.bufferData->barrierFrameNumber);
- ready = TransactionReadiness::NotReadyBarrier;
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
- }
- }
-
- // If backpressure is enabled and we already have a buffer to commit, keep
- // the transaction in the queue.
- const bool hasPendingBuffer =
- flushState.bufferLayersReadyToPresent.contains(s.surface.get());
- if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
- ATRACE_FORMAT("hasPendingBuffer %s", layer->getDebugName());
- ready = TransactionReadiness::NotReady;
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
-
- const bool acquireFenceAvailable = s.bufferData &&
- s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
- s.bufferData->acquireFence;
- const bool fenceSignaled = !acquireFenceAvailable ||
- s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
- if (!fenceSignaled) {
- // check fence status
- const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
- flushState.firstTransaction) &&
- layer->isSimpleBufferUpdate(s);
-
- if (allowLatchUnsignaled) {
- ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
- layer->getDebugName());
- ready = TransactionReadiness::NotReadyUnsignaled;
- } else {
- ready = TransactionReadiness::NotReady;
- auto& listener = s.bufferData->releaseBufferListener;
- if (listener &&
- (flushState.queueProcessTime - transaction.postTime) >
- std::chrono::nanoseconds(4s).count()) {
- // Used to add a stalled transaction which uses an internal lock.
- ftl::FakeGuard guard(kMainThreadContext);
- mTransactionHandler
- .onTransactionQueueStalled(transaction.id,
- {.pid = layer->getOwnerPid(),
- .layerId = static_cast<uint32_t>(
- layer->getSequence()),
- .layerName = layer->getDebugName(),
- .bufferId = s.bufferData->getId(),
- .frameNumber = s.bufferData->frameNumber});
- }
- ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
- }
- return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
- });
- return ready;
-}
-
TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
@@ -4988,8 +4482,8 @@
uint32_t currentMaxAcquiredBufferCount =
getMaxAcquiredBufferCountForCurrentRefreshRate(
layer->ownerUid.val());
- ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
- layer->name.c_str(), s.bufferData->frameNumber);
+ SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
+ layer->name.c_str(), s.bufferData->frameNumber);
s.bufferData->releaseBufferListener
->onReleaseBuffer({resolvedState.externalTexture->getBuffer()
->getId(),
@@ -5003,9 +4497,9 @@
// Delete the entire state at this point and not just release the buffer
// because everything associated with the Layer in this Transaction is now
// out of date.
- ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
- layer->name.c_str(), layer->barrierProducerId,
- s.bufferData->producerId);
+ SFTRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
+ layer->name.c_str(), layer->barrierProducerId,
+ s.bufferData->producerId);
return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
}
@@ -5015,10 +4509,10 @@
((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
s.bufferData->barrierFrameNumber));
if (!willApplyBarrierFrame) {
- ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64
- " > %" PRId64,
- layer->name.c_str(), layer->barrierFrameNumber,
- s.bufferData->barrierFrameNumber);
+ SFTRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64
+ " > %" PRId64,
+ layer->name.c_str(), layer->barrierFrameNumber,
+ s.bufferData->barrierFrameNumber);
ready = TransactionReadiness::NotReadyBarrier;
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
@@ -5031,7 +4525,7 @@
flushState.bufferLayersReadyToPresent.contains(s.surface.get());
if (layer->backpressureEnabled() && hasPendingBuffer &&
transaction.isAutoTimestamp) {
- ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
+ SFTRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
ready = TransactionReadiness::NotReady;
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
@@ -5048,8 +4542,8 @@
flushState.firstTransaction) &&
layer->isSimpleBufferUpdate(s);
if (allowLatchUnsignaled) {
- ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
- layer->name.c_str());
+ SFTRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
+ layer->name.c_str());
ready = TransactionReadiness::NotReadyUnsignaled;
} else {
ready = TransactionReadiness::NotReady;
@@ -5066,7 +4560,7 @@
.frameNumber =
s.bufferData->frameNumber});
}
- ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
+ SFTRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
}
@@ -5078,15 +4572,8 @@
void SurfaceFlinger::addTransactionReadyFilters() {
mTransactionHandler.addTransactionReadyFilter(
std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
- if (mLayerLifecycleManagerEnabled) {
- mTransactionHandler.addTransactionReadyFilter(
- std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this,
- std::placeholders::_1));
- } else {
- mTransactionHandler.addTransactionReadyFilter(
- std::bind(&SurfaceFlinger::transactionReadyBufferCheckLegacy, this,
- std::placeholders::_1));
- }
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
}
// For tests only
@@ -5145,22 +4632,22 @@
bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t numStates,
bool firstTransaction) const {
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
- ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
+ SFTRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
return false;
}
// We only want to latch unsignaled when a single layer is updated in this
// transaction (i.e. not a blast sync transaction).
if (numStates != 1) {
- ATRACE_FORMAT_INSTANT("%s: false (numStates=%zu)", __func__, numStates);
+ SFTRACE_FORMAT_INSTANT("%s: false (numStates=%zu)", __func__, numStates);
return false;
}
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
if (!firstTransaction) {
- ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first "
- "transaction)",
- __func__);
+ SFTRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first "
+ "transaction)",
+ __func__);
return false;
}
@@ -5168,9 +4655,9 @@
// as it leads to jank due to RenderEngine waiting for unsignaled buffer
// or window animations being slow.
if (mScheduler->vsyncModulator().isVsyncConfigEarly()) {
- ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; "
- "isVsyncConfigEarly)",
- __func__);
+ SFTRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::AutoSingleLayer; "
+ "isVsyncConfigEarly)",
+ __func__);
return false;
}
}
@@ -5180,12 +4667,12 @@
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
const std::vector<uint64_t>& mergedTransactionIds) {
- ATRACE_CALL();
+ SFTRACE_CALL();
IPCThreadState* ipc = IPCThreadState::self();
const int originPid = ipc->getCallingPid();
@@ -5195,7 +4682,7 @@
composerState.state.sanitize(permissions);
}
- for (DisplayState display : displays) {
+ for (DisplayState& display : displays) {
display.sanitize(permissions);
}
@@ -5316,11 +4803,6 @@
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
- if (!mLayerLifecycleManagerEnabled) {
- for (DisplayState& display : displays) {
- transactionFlags |= setDisplayStateLocked(display);
- }
- }
// start and end registration for listeners w/ no surface so they can get their callback. Note
// that listeners with SurfaceControls will start registration during setClientStateLocked
@@ -5328,27 +4810,11 @@
for (const auto& listener : listenerCallbacks) {
mTransactionCallbackInvoker.addEmptyTransaction(listener);
}
- nsecs_t now = systemTime();
uint32_t clientStateFlags = 0;
for (auto& resolvedState : states) {
clientStateFlags |=
updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState, desiredPresentTime,
isAutoTimestamp, postTime, transactionId);
- if (!mLayerLifecycleManagerEnabled) {
- if ((flags & eAnimation) && resolvedState.state.surface) {
- if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
- const auto layerProps = scheduler::LayerProps{
- .visible = layer->isVisible(),
- .bounds = layer->getBounds(),
- .transform = layer->getTransform(),
- .setFrameRateVote = layer->getFrameRateForLayerTree(),
- .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
- .isFrontBuffered = layer->isFrontBuffered(),
- };
- layer->recordLayerHistoryAnimationTx(layerProps, now);
- }
- }
- }
}
transactionFlags |= clientStateFlags;
@@ -5483,366 +4949,6 @@
return true;
}
-uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
- ResolvedComposerState& composerState,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint64_t transactionId) {
- layer_state_t& s = composerState.state;
-
- std::vector<ListenerCallbacks> filteredListeners;
- for (auto& listener : s.listeners) {
- // Starts a registration but separates the callback ids according to callback type. This
- // allows the callback invoker to send on latch callbacks earlier.
- // note that startRegistration will not re-register if the listener has
- // already be registered for a prior surface control
-
- ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
- if (!onCommitCallbacks.callbackIds.empty()) {
- filteredListeners.push_back(onCommitCallbacks);
- }
-
- ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
- if (!onCompleteCallbacks.callbackIds.empty()) {
- filteredListeners.push_back(onCompleteCallbacks);
- }
- }
-
- const uint64_t what = s.what;
- uint32_t flags = 0;
- sp<Layer> layer = nullptr;
- if (s.surface) {
- layer = LayerHandle::getLayer(s.surface);
- } else {
- // The client may provide us a null handle. Treat it as if the layer was removed.
- ALOGW("Attempt to set client state with a null layer handle");
- }
- if (layer == nullptr) {
- for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener,
- callbackIds,
- s.surface),
- std::vector<JankData>());
- }
- return 0;
- }
- MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
-
- ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current);
-
- // Only set by BLAST adapter layers
- if (what & layer_state_t::eProducerDisconnect) {
- layer->onDisconnect();
- }
-
- if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eLayerChanged) {
- // NOTE: index needs to be calculated before we update the state
- const auto& p = layer->getParent();
- if (p == nullptr) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setLayer(s.z) && idx >= 0) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- } else {
- if (p->setChildLayer(layer, s.z)) {
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- }
- if (what & layer_state_t::eRelativeLayerChanged) {
- // NOTE: index needs to be calculated before we update the state
- const auto& p = layer->getParent();
- const auto& relativeHandle = s.relativeLayerSurfaceControl ?
- s.relativeLayerSurfaceControl->getHandle() : nullptr;
- if (p == nullptr) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setRelativeLayer(relativeHandle, s.z) &&
- idx >= 0) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- } else {
- if (p->setChildRelativeLayer(layer, relativeHandle, s.z)) {
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- }
- if (what & layer_state_t::eAlphaChanged) {
- if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eColorChanged) {
- if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eColorTransformChanged) {
- if (layer->setColorTransform(s.colorTransform)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eBackgroundColorChanged) {
- if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eMatrixChanged) {
- if (layer->setMatrix(s.matrix)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTransparentRegionChanged) {
- if (layer->setTransparentRegionHint(s.transparentRegion))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eFlagsChanged) {
- if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eCornerRadiusChanged) {
- if (layer->setCornerRadius(s.cornerRadius))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eBackgroundBlurRadiusChanged && mSupportsBlur) {
- if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eBlurRegionsChanged) {
- if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eLayerStackChanged) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- // We only allow setting layer stacks for top level layers,
- // everything else inherits layer stack from its parent.
- if (layer->hasParent()) {
- ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid",
- layer->getDebugName());
- } else if (idx < 0) {
- ALOGE("Attempt to set layer stack on layer without parent (%s) that "
- "that also does not appear in the top level layer list. Something"
- " has gone wrong.",
- layer->getDebugName());
- } else if (layer->setLayerStack(s.layerStack)) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
- }
- }
- if (what & layer_state_t::eBufferTransformChanged) {
- if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTransformToDisplayInverseChanged) {
- if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eCropChanged) {
- if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eDataspaceChanged) {
- if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eSurfaceDamageRegionChanged) {
- if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eApiChanged) {
- if (layer->setApi(s.api)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eSidebandStreamChanged) {
- if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eInputInfoChanged) {
- layer->setInputInfo(*s.windowInfoHandle->getInfo());
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eMetadataChanged) {
- if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
- gameMode != -1) {
- // The transaction will be received on the Task layer and needs to be applied to all
- // child layers. Child layers that are added at a later point will obtain the game mode
- // info through addChild().
- layer->setGameModeForTree(static_cast<GameMode>(gameMode));
- }
-
- if (layer->setMetadata(s.metadata)) {
- flags |= eTraversalNeeded;
- mLayerMetadataSnapshotNeeded = true;
- }
- }
- if (what & layer_state_t::eColorSpaceAgnosticChanged) {
- if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eShadowRadiusChanged) {
- if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
- const auto compatibility =
- Layer::FrameRate::convertCompatibility(s.defaultFrameRateCompatibility);
-
- if (layer->setDefaultFrameRateCompatibility(compatibility)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateSelectionPriority) {
- if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateChanged) {
- const auto compatibility =
- Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
- const auto strategy =
- Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
-
- if (layer->setFrameRate(Layer::FrameRate::FrameRateVote(Fps::fromValue(s.frameRate),
- compatibility, strategy))) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateCategoryChanged) {
- const FrameRateCategory category = Layer::FrameRate::convertCategory(s.frameRateCategory);
- if (layer->setFrameRateCategory(category, s.frameRateCategorySmoothSwitchOnly)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateSelectionStrategyChanged) {
- const scheduler::LayerInfo::FrameRateSelectionStrategy strategy =
- scheduler::LayerInfo::convertFrameRateSelectionStrategy(
- s.frameRateSelectionStrategy);
- if (layer->setFrameRateSelectionStrategy(strategy)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFixedTransformHintChanged) {
- if (layer->setFixedTransformHint(s.fixedTransformHint)) {
- flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
- }
- }
- if (what & layer_state_t::eAutoRefreshChanged) {
- layer->setAutoRefresh(s.autoRefresh);
- }
- if (what & layer_state_t::eDimmingEnabledChanged) {
- if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
- if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDesiredHdrHeadroomChanged) {
- if (layer->setDesiredHdrHeadroom(s.desiredHdrSdrRatio)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eCachingHintChanged) {
- if (layer->setCachingHint(s.cachingHint)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eHdrMetadataChanged) {
- if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTrustedOverlayChanged) {
- if (layer->setTrustedOverlay(s.trustedOverlay == gui::TrustedOverlay::ENABLED)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eStretchChanged) {
- if (layer->setStretchEffect(s.stretchEffect)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eBufferCropChanged) {
- if (layer->setBufferCrop(s.bufferCrop)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDestinationFrameChanged) {
- if (layer->setDestinationFrame(s.destinationFrame)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDropInputModeChanged) {
- if (layer->setDropInputMode(s.dropInputMode)) {
- flags |= eTraversalNeeded;
- mUpdateInputInfo = true;
- }
- }
- // This has to happen after we reparent children because when we reparent to null we remove
- // child layers from current state and remove its relative z. If the children are reparented in
- // the same transaction, then we have to make sure we reparent the children first so we do not
- // lose its relative z order.
- if (what & layer_state_t::eReparent) {
- bool hadParent = layer->hasParent();
- auto parentHandle = (s.parentSurfaceControlForChild)
- ? s.parentSurfaceControlForChild->getHandle()
- : nullptr;
- if (layer->reparent(parentHandle)) {
- if (!hadParent) {
- layer->setIsAtRoot(false);
- mCurrentState.layersSortedByZ.remove(layer);
- }
- flags |= eTransactionNeeded | eTraversalNeeded;
- }
- }
- std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
- for (auto& [listener, callbackIds] : filteredListeners) {
- callbackHandles.emplace_back(
- sp<CallbackHandle>::make(listener, callbackIds, s.surface));
- }
- }
-
- if (what & layer_state_t::eBufferChanged) {
- if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
- flags |= eTraversalNeeded;
- }
- } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
- }
-
- if ((what & layer_state_t::eBufferChanged) == 0) {
- layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
- }
-
- if (what & layer_state_t::eTrustedPresentationInfoChanged) {
- if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
- s.trustedPresentationListener)) {
- flags |= eTraversalNeeded;
- }
- }
-
- if (what & layer_state_t::eFlushJankData) {
- // Do nothing. Processing the transaction completed listeners currently cause the flush.
- }
-
- if (layer->setTransactionCompletedListeners(callbackHandles,
- layer->willPresentCurrentTransaction() ||
- layer->willReleaseBufferOnLatch())) {
- flags |= eTraversalNeeded;
- }
-
- // Do not put anything that updates layer state or modifies flags after
- // setTransactionCompletedListener
-
- // if the layer has been parented on to a new display, update its transform hint.
- if (((flags & eTransformHintUpdateNeeded) == 0) &&
- oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) {
- flags |= eTransformHintUpdateNeeded;
- }
-
- return flags;
-}
-
uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& frameTimelineInfo,
ResolvedComposerState& composerState,
int64_t desiredPresentTime,
@@ -5879,10 +4985,8 @@
}
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener,
- callbackIds,
- s.surface),
- std::vector<JankData>());
+ mTransactionCallbackInvoker.addCallbackHandle(
+ sp<CallbackHandle>::make(listener, callbackIds, s.surface));
}
return 0;
}
@@ -5897,6 +5001,17 @@
sp<CallbackHandle>::make(listener, callbackIds, s.surface));
}
}
+
+ frontend::LayerSnapshot* snapshot = nullptr;
+ gui::GameMode gameMode = gui::GameMode::Unsupported;
+ if (what & (layer_state_t::eSidebandStreamChanged | layer_state_t::eBufferChanged) ||
+ frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
+ if (snapshot) {
+ gameMode = snapshot->gameMode;
+ }
+ }
+
// TODO(b/238781169) remove after screenshot refactor, currently screenshots
// requires to read drawing state from binder thread. So we need to fix that
// before removing this.
@@ -5911,7 +5026,7 @@
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eSidebandStreamChanged) {
- if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime))
+ if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime, gameMode))
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eDataspaceChanged) {
@@ -5929,18 +5044,17 @@
}
if (what & layer_state_t::eBufferChanged) {
std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
- frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
if (snapshot) {
transformHint = snapshot->transformHint;
}
layer->setTransformHint(transformHint);
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
+ desiredPresentTime, isAutoTimestamp, frameTimelineInfo, gameMode)) {
flags |= eTraversalNeeded;
}
- mLayersWithQueuedFrames.emplace(layer);
+ mLayersWithQueuedFrames.emplace(layer, gameMode);
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime, gameMode);
}
if ((what & layer_state_t::eBufferChanged) == 0) {
@@ -5954,6 +5068,10 @@
}
}
+ if (what & layer_state_t::eBufferReleaseChannelChanged) {
+ layer->setBufferReleaseChannel(s.bufferReleaseChannel);
+ }
+
const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence());
bool willPresentCurrentTransaction = requestedLayerState &&
(requestedLayerState->hasReadyFrame() ||
@@ -5992,8 +5110,6 @@
if (result != NO_ERROR) {
return result;
}
-
- mirrorLayer->setClonedChild(mirrorFrom->createClone());
}
outResult.layerId = mirrorLayer->sequence;
@@ -6107,31 +5223,22 @@
return NO_ERROR;
}
-void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) {
- mLayersPendingRemoval.add(layer);
- mLayersRemoved = true;
- setTransactionFlags(eTransactionNeeded);
-}
-
void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
{
- std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- mDestroyedHandles.emplace_back(layerId, layer->getDebugName());
- }
-
- {
// Used to remove stalled transactions which uses an internal lock.
ftl::FakeGuard guard(kMainThreadContext);
mTransactionHandler.onLayerDestroyed(layerId);
}
+ JankTracker::flushJankData(layerId);
- Mutex::Autolock lock(mStateLock);
- markLayerPendingRemovalLocked(layer);
+ std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+ mDestroyedHandles.emplace_back(layerId, layer->getDebugName());
+
+ Mutex::Autolock stateLock(mStateLock);
layer->onHandleDestroyed();
mBufferCountTracker.remove(handle);
layer.clear();
-
- setTransactionFlags(eTransactionFlushNeeded);
+ setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded);
}
void SurfaceFlinger::initializeDisplays() {
@@ -6402,15 +5509,23 @@
return NO_ERROR;
}
- // Traversal of drawing state must happen on the main thread.
- // Otherwise, SortedVector may have shared ownership during concurrent
- // traversals, which can result in use-after-frees.
+ // Collect debug data from main thread
std::string compositionLayers;
mScheduler
->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
dumpVisibleFrontEnd(compositionLayers);
})
.get();
+ // get window info listener data without the state lock
+ auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
+ compositionLayers.append("Window Infos:\n");
+ StringAppendF(&compositionLayers, " max send vsync id: %" PRId64 "\n",
+ ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
+ StringAppendF(&compositionLayers, " max send delay (ns): %" PRId64 " ns\n",
+ windowInfosDebug.maxSendDelayDuration);
+ StringAppendF(&compositionLayers, " unsent messages: %zu\n",
+ windowInfosDebug.pendingMessageCount);
+ compositionLayers.append("\n");
dumpAll(args, compositionLayers, result);
write(fd, result.c_str(), result.size());
return NO_ERROR;
@@ -6427,7 +5542,7 @@
}
void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const {
- StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
+ StringAppendF(&result, "%" PRId64 "\n", mScheduler->getPacesetterVsyncPeriod().ns());
if (args.size() < 2) return;
const auto name = String8(args[1]);
@@ -6462,8 +5577,8 @@
if (now - sTimestamp < 30min) return;
sTimestamp = now;
- ATRACE_CALL();
- mDrawingState.traverse([&](Layer* layer) { layer->logFrameStats(); });
+ SFTRACE_CALL();
+ traverseLegacyLayers([&](Layer* layer) { layer->logFrameStats(); });
}
void SurfaceFlinger::appendSfConfigString(std::string& result) const {
@@ -6487,11 +5602,6 @@
// TODO(b/241285876): Move to DisplayModeController.
dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
dumper.eol();
-
- StringAppendF(&result,
- " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64
- " ns\n\n",
- dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
}
void SurfaceFlinger::dumpEvents(std::string& result) const {
@@ -6654,53 +5764,35 @@
}
void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) {
- if (!mLayerLifecycleManagerEnabled) {
- StringAppendF(&result, "Composition layers\n");
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto* compositionState = layer->getCompositionState();
- if (!compositionState || !compositionState->isVisible) return;
- android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
- layer->getDebugName() ? layer->getDebugName()
- : "<unknown>");
- compositionState->dump(result);
- });
-
- StringAppendF(&result, "Offscreen Layers\n");
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
- }
- } else {
- std::ostringstream out;
- out << "\nComposition list\n";
- ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
- if (snapshot->hasSomethingToDraw()) {
- if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
- lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
- }
- out << " " << *snapshot << "\n";
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (snapshot->hasSomethingToDraw()) {
+ if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
}
- });
+ out << " " << *snapshot << "\n";
+ }
+ });
- out << "\nInput list\n";
- lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
- lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
- }
- out << " " << snapshot << "\n";
- });
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
- out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy() << "\nOffscreen Hierarchy\n"
- << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
- result = out.str();
- dumpHwcLayersMinidump(result);
- }
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy() << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
+ result = out.str();
+ dumpHwcLayersMinidump(result);
}
perfetto::protos::LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
@@ -6715,9 +5807,16 @@
}
}
- return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
- mLegacyLayers, traceFlags)
- .generate(mLayerHierarchyBuilder.getHierarchy());
+ auto traceGenerator =
+ LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
+ mLegacyLayers, traceFlags)
+ .with(mLayerHierarchyBuilder.getHierarchy());
+
+ if (traceFlags & LayerTracing::Flag::TRACE_EXTRA) {
+ traceGenerator.withOffscreenLayers(mLayerHierarchyBuilder.getOffscreenHierarchy());
+ }
+
+ return traceGenerator.generate();
}
google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto>
@@ -6751,26 +5850,6 @@
getHwComposer().dump(result);
}
-void SurfaceFlinger::dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags) const {
- // Add a fake invisible root layer to the proto output and parent all the offscreen layers to
- // it.
- perfetto::protos::LayerProto* rootProto = layersProto.add_layers();
- const int32_t offscreenRootLayerId = INT32_MAX - 2;
- rootProto->set_id(offscreenRootLayerId);
- rootProto->set_name("Offscreen Root");
- rootProto->set_parent(-1);
-
- for (Layer* offscreenLayer : mOffscreenLayers) {
- // Add layer as child of the fake root
- rootProto->add_children(offscreenLayer->sequence);
-
- // Add layer
- auto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
- layerProto->set_parent(offscreenRootLayerId);
- }
-}
-
perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
return mScheduler
->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
@@ -6779,41 +5858,7 @@
.get();
}
-void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
- auto future = mScheduler->schedule([this] {
- std::string result;
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
- }
- return result;
- });
-
- result.append("Offscreen Layers:\n");
- result.append(future.get());
-}
-
-void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const {
- for (const auto& [token, display] : mDisplays) {
- const auto displayId = HalDisplayId::tryCast(display->getId());
- if (!displayId) {
- continue;
- }
-
- StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
- displayId == mActiveDisplayId ? "active" : "inactive");
- Layer::miniDumpHeader(result);
-
- const DisplayDevice& ref = *display;
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDumpLegacy(result, ref); });
- result.append("\n");
- }
-}
-
void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
- if (!mLayerLifecycleManagerEnabled) {
- return dumpHwcLayersMinidumpLockedLegacy(result);
- }
for (const auto& [token, display] : mDisplays) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
@@ -6893,8 +5938,7 @@
* Dump the visible layer list
*/
colorizer.bold(result);
- StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n",
- mLayerLifecycleManagerEnabled ? "true" : "false");
+ StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n", "true");
StringAppendF(&result, "Active Layers - layers with client handles (count = %zu)\n",
mNumLayers.load());
colorizer.reset(result);
@@ -6993,15 +6037,6 @@
result.append(mTimeStats->miniDump());
result.append("\n");
-
- result.append("Window Infos:\n");
- auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
- StringAppendF(&result, " max send vsync id: %" PRId64 "\n",
- ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
- StringAppendF(&result, " max send delay (ns): %" PRId64 " ns\n",
- windowInfosDebug.maxSendDelayDuration);
- StringAppendF(&result, " unsent messages: %zu\n", windowInfosDebug.pendingMessageCount);
- result.append("\n");
}
mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
@@ -7495,15 +6530,9 @@
return NO_ERROR;
}
case 1039: {
- PhysicalDisplayId displayId = [&]() {
- Mutex::Autolock lock(mStateLock);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- auto inUid = static_cast<uid_t>(data.readInt32());
+ const auto uid = static_cast<uid_t>(data.readInt32());
const auto refreshRate = data.readFloat();
- mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
+ mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{uid, refreshRate});
return NO_ERROR;
}
// Toggle caching feature
@@ -7692,7 +6721,7 @@
// Update the overlay on the main thread to avoid race conditions with
// RefreshRateSelector::getActiveMode
- static_cast<void>(mScheduler->schedule([=, this] {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
@@ -7700,15 +6729,9 @@
}
if (!display->isRefreshRateOverlayEnabled()) return;
- const auto desiredModeIdOpt =
- mDisplayModeController.getDesiredMode(display->getPhysicalId())
- .transform([](const display::DisplayModeRequest& request) {
- return request.mode.modePtr->getId();
- });
+ const auto state = mDisplayModeController.getKernelIdleTimerState(display->getPhysicalId());
- const bool timerExpired = mKernelIdleTimerEnabled && expired;
-
- if (display->onKernelTimerChanged(desiredModeIdOpt, timerExpired)) {
+ if (display->onKernelTimerChanged(state.desiredModeIdOpt, state.isEnabled && expired)) {
mScheduler->scheduleFrame();
}
}));
@@ -7730,8 +6753,8 @@
}));
}
-std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
-SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
+auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId)
+ -> std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> {
const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
android::Hwc2::Composer::OptionalFeature::KernelIdleTimer);
const auto timeout = getIdleTimerTimeout(displayId);
@@ -7755,63 +6778,6 @@
return {std::nullopt, timeout};
}
-void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout,
- KernelIdleTimerController controller,
- PhysicalDisplayId displayId) {
- switch (controller) {
- case KernelIdleTimerController::HwcApi: {
- getHwComposer().setIdleTimerEnabled(displayId, timeout);
- break;
- }
- case KernelIdleTimerController::Sysprop: {
- base::SetProperty(KERNEL_IDLE_TIMER_PROP, timeout > 0ms ? "true" : "false");
- break;
- }
- }
-}
-
-void SurfaceFlinger::toggleKernelIdleTimer() {
- using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
-
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display) {
- ALOGW("%s: default display is null", __func__);
- return;
- }
-
- // If the support for kernel idle timer is disabled for the active display,
- // don't do anything.
- const std::optional<KernelIdleTimerController> kernelIdleTimerController =
- display->refreshRateSelector().kernelIdleTimerController();
- if (!kernelIdleTimerController.has_value()) {
- return;
- }
-
- const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction();
-
- switch (action) {
- case KernelIdleTimerAction::TurnOff:
- if (mKernelIdleTimerEnabled) {
- ATRACE_INT("KernelIdleTimer", 0);
- std::chrono::milliseconds constexpr kTimerDisabledTimeout = 0ms;
- updateKernelIdleTimer(kTimerDisabledTimeout, kernelIdleTimerController.value(),
- display->getPhysicalId());
- mKernelIdleTimerEnabled = false;
- }
- break;
- case KernelIdleTimerAction::TurnOn:
- if (!mKernelIdleTimerEnabled) {
- ATRACE_INT("KernelIdleTimer", 1);
- const std::chrono::milliseconds timeout =
- display->refreshRateSelector().getIdleTimerTimeout();
- updateKernelIdleTimer(timeout, kernelIdleTimerController.value(),
- display->getPhysicalId());
- mKernelIdleTimerEnabled = true;
- }
- break;
- }
-}
-
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -7837,7 +6803,8 @@
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ if (uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+ PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
return OK;
}
@@ -7930,9 +6897,10 @@
void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
- ATRACE_CALL();
+ SFTRACE_CALL();
- status_t validate = validateScreenshotPermissions(args);
+ const auto& captureArgs = args.captureArgs;
+ status_t validate = validateScreenshotPermissions(captureArgs);
if (validate != OK) {
ALOGD("Permission denied to captureDisplay");
invokeScreenCaptureError(validate, captureListener);
@@ -7945,7 +6913,7 @@
return;
}
- if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
+ if (captureArgs.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
return;
@@ -7971,7 +6939,7 @@
reqSize = display->getLayerStackSpaceRect().getSize();
}
- for (const auto& handle : args.excludeHandles) {
+ for (const auto& handle : captureArgs.excludeHandles) {
uint32_t excludeLayer = LayerHandle::getLayerId(handle);
if (excludeLayer != UNASSIGNED_LAYER_ID) {
excludeLayerIds.emplace(excludeLayer);
@@ -7984,17 +6952,22 @@
}
GetLayerSnapshotsFunction getLayerSnapshotsFn =
- getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
+ getLayerSnapshotsForScreenshots(layerStack, captureArgs.uid,
+ std::move(excludeLayerIds));
ftl::Flags<RenderArea::Options> options;
- if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
- if (args.hintForSeamlessTransition)
+ if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
+ if (captureArgs.hintForSeamlessTransition)
options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
- args.sourceCrop, reqSize, args.dataspace,
+ gui::aidl_utils::fromARect(captureArgs.sourceCrop),
+ reqSize,
+ static_cast<ui::Dataspace>(captureArgs.dataspace),
displayWeak, options),
- getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
- args.grayscale, captureListener);
+ getLayerSnapshotsFn, reqSize,
+ static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
+ captureArgs.allowProtected, captureArgs.grayscale,
+ captureArgs.attachGainmap, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -8047,10 +7020,11 @@
if (args.hintForSeamlessTransition)
options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
- Rect(), size, args.dataspace, displayWeak,
- options),
- getLayerSnapshotsFn, size, args.pixelFormat, kAllowProtected, kGrayscale,
- captureListener);
+ Rect(), size,
+ static_cast<ui::Dataspace>(args.dataspace),
+ displayWeak, options),
+ getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat),
+ kAllowProtected, kGrayscale, args.attachGainmap, captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -8061,22 +7035,25 @@
void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
- ATRACE_CALL();
+ SFTRACE_CALL();
- status_t validate = validateScreenshotPermissions(args);
+ const auto& captureArgs = args.captureArgs;
+
+ status_t validate = validateScreenshotPermissions(captureArgs);
if (validate != OK) {
ALOGD("Permission denied to captureLayers");
invokeScreenCaptureError(validate, captureListener);
return;
}
+ auto crop = gui::aidl_utils::fromARect(captureArgs.sourceCrop);
+
ui::Size reqSize;
sp<Layer> parent;
- Rect crop(args.sourceCrop);
std::unordered_set<uint32_t> excludeLayerIds;
- ui::Dataspace dataspace = args.dataspace;
+ ui::Dataspace dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace);
- if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
+ if (captureArgs.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
return;
@@ -8093,26 +7070,27 @@
}
Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState());
- if (args.sourceCrop.width() <= 0) {
+ if (crop.width() <= 0) {
crop.left = 0;
crop.right = parentSourceBounds.getWidth();
}
- if (args.sourceCrop.height() <= 0) {
+ if (crop.height() <= 0) {
crop.top = 0;
crop.bottom = parentSourceBounds.getHeight();
}
- if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) {
+ if (crop.isEmpty() || captureArgs.frameScaleX <= 0.0f || captureArgs.frameScaleY <= 0.0f) {
// Error out if the layer has no source bounds (i.e. they are boundless) and a source
// crop was not specified, or an invalid frame scale was provided.
ALOGD("Boundless layer, unspecified crop, or invalid frame scale to captureLayers");
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
- reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
+ reqSize = ui::Size(crop.width() * captureArgs.frameScaleX,
+ crop.height() * captureArgs.frameScaleY);
- for (const auto& handle : args.excludeHandles) {
+ for (const auto& handle : captureArgs.excludeHandles) {
uint32_t excludeLayer = LayerHandle::getLayerId(handle);
if (excludeLayer != UNASSIGNED_LAYER_ID) {
excludeLayerIds.emplace(excludeLayer);
@@ -8138,8 +7116,9 @@
}
GetLayerSnapshotsFunction getLayerSnapshotsFn =
- getLayerSnapshotsForScreenshots(parent->sequence, args.uid, std::move(excludeLayerIds),
- args.childrenOnly, parentCrop);
+ getLayerSnapshotsForScreenshots(parent->sequence, captureArgs.uid,
+ std::move(excludeLayerIds), args.childrenOnly,
+ parentCrop);
if (captureListener == nullptr) {
ALOGD("capture screen must provide a capture listener callback");
@@ -8148,14 +7127,16 @@
}
ftl::Flags<RenderArea::Options> options;
- if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
- if (args.hintForSeamlessTransition)
+ if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
+ if (captureArgs.hintForSeamlessTransition)
options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop,
reqSize, dataspace, parent, args.childrenOnly,
options),
- getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
- args.grayscale, captureListener);
+ getLayerSnapshotsFn, reqSize,
+ static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
+ captureArgs.allowProtected, captureArgs.grayscale,
+ captureArgs.attachGainmap, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
@@ -8164,9 +7145,7 @@
void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE,
ui::LayerStack layerStack) {
ftl::Future<FenceResult> futureFence = layerFE->createReleaseFenceFuture();
- Layer* clonedFrom = layer->getClonedFrom().get();
- auto owningLayer = clonedFrom ? clonedFrom : layer;
- owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack);
+ layer->prepareReleaseCallbacks(std::move(futureFence), layerStack);
}
// Loop over all visible layers to see whether there's any protected layer. A protected layer is
@@ -8189,12 +7168,12 @@
// Accessing display requires mStateLock, and contention for this lock
// is reduced when grabbed from the main thread, thus also reducing
// risk of deadlocks.
-std::optional<SurfaceFlinger::OutputCompositionState>
-SurfaceFlinger::getDisplayAndLayerSnapshotsFromMainThread(
+std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread(
RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
std::vector<sp<LayerFE>>& layerFEs) {
return mScheduler
->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) {
+ SFTRACE_NAME("getSnapshotsFromMainThread");
auto layers = getLayerSnapshotsFn();
for (auto& [layer, layerFE] : layers) {
attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
@@ -8208,9 +7187,9 @@
void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
GetLayerSnapshotsFunction getLayerSnapshotsFn,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- bool allowProtected, bool grayscale,
+ bool allowProtected, bool grayscale, bool attachGainmap,
const sp<IScreenCaptureListener>& captureListener) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
@@ -8224,8 +7203,7 @@
FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
std::vector<sp<LayerFE>> layerFEs;
auto displayState =
- getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn,
- layerFEs);
+ getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs);
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
@@ -8255,9 +7233,9 @@
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
- auto futureFence =
- captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
- isProtected, captureListener, displayState, layerFEs);
+ auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */,
+ grayscale, isProtected, attachGainmap, captureListener,
+ displayState, layerFEs);
futureFence.get();
} else {
@@ -8292,7 +7270,7 @@
WRITEABLE);
auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
false /* regionSampling */, grayscale,
- isProtected, captureListener);
+ isProtected, attachGainmap, captureListener);
futureFence.get();
}
}
@@ -8346,9 +7324,10 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>& captureListener,
std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ScreenCaptureResults captureResults;
std::unique_ptr<const RenderArea> renderArea =
@@ -8363,19 +7342,87 @@
}
return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
}
+ float displayBrightnessNits = displayState.value().displayBrightnessNits;
+ float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
// Empty vector needed to pass into renderScreenImpl for legacy path
std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
- captureResults, displayState, layers, layerFEs);
+ renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
+ attachGainmap, captureResults, displayState, layers, layerFEs);
+
+ if (captureResults.capturedHdrLayers && attachGainmap &&
+ FlagManager::getInstance().true_hdr_screenshots()) {
+ sp<GraphicBuffer> hdrBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-hdr");
+ sp<GraphicBuffer> gainmapBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ buffer->getPixelFormat(), 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-gainmap");
+
+ const status_t bufferStatus = hdrBuffer->initCheck();
+ const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
+
+ if (bufferStatus != OK) {
+ ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__,
+ bufferStatus);
+ } else if (gainmapBufferStatus != OK) {
+ ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
+ __func__, gainmapBufferStatus);
+ } else {
+ captureResults.optionalGainMap = gainmapBuffer;
+ const auto hdrTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ const auto gainmapTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ ScreenCaptureResults unusedResults;
+ ftl::SharedFuture<FenceResult> hdrRenderFuture =
+ renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
+ isProtected, attachGainmap, unusedResults, displayState,
+ layers, layerFEs);
+
+ renderFuture =
+ ftl::Future(std::move(renderFuture))
+ .then([&, hdrRenderFuture = std::move(hdrRenderFuture),
+ displayBrightnessNits, sdrWhitePointNits,
+ dataspace = captureResults.capturedDataspace, buffer, hdrTexture,
+ gainmapTexture](FenceResult fenceResult) -> FenceResult {
+ if (!fenceResult.ok()) {
+ return fenceResult;
+ }
+
+ auto hdrFenceResult = hdrRenderFuture.get();
+
+ if (!hdrFenceResult.ok()) {
+ return hdrFenceResult;
+ }
+
+ return getRenderEngine()
+ .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture,
+ hdrFenceResult.value()->get(),
+ displayBrightnessNits / sdrWhitePointNits,
+ static_cast<ui::Dataspace>(dataspace),
+ gainmapTexture)
+ .get();
+ })
+ .share();
+ };
+ }
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
+ .then([captureListener, captureResults = std::move(captureResults),
+ displayBrightnessNits,
+ sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult {
captureResults.fenceResult = std::move(fenceResult);
+ captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits;
captureListener->onScreenCaptureCompleted(captureResults);
return base::unexpected(NO_ERROR);
})
@@ -8387,8 +7434,9 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
- ATRACE_CALL();
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>& captureListener) {
+ SFTRACE_CALL();
auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
@@ -8416,8 +7464,8 @@
auto layerFEs = extractLayerFEs(layers);
ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
- isProtected, captureResults, displayState, layers, layerFEs);
+ renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
+ attachGainmap, captureResults, displayState, layers, layerFEs);
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
@@ -8447,12 +7495,11 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- std::unique_ptr<const RenderArea> renderArea,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
- std::optional<OutputCompositionState>& displayState,
+ const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
+ ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState,
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
for (auto& layerFE : layerFEs) {
frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
@@ -8628,65 +7675,13 @@
}
void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) const {
- if (mLayerLifecycleManagerEnabled) {
- for (auto& layer : mLegacyLayers) {
- visitor(layer.second.get());
- }
- } else {
- mDrawingState.traverse(visitor);
+ for (auto& layer : mLegacyLayers) {
+ visitor(layer.second.get());
}
}
// ---------------------------------------------------------------------------
-void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
- layersSortedByZ.traverse(visitor);
-}
-
-void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
- layersSortedByZ.traverseInZOrder(stateSet, visitor);
-}
-
-void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& visitor) const {
- layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
-}
-
-void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
- std::unordered_set<uint32_t> excludeLayerIds,
- const LayerVector::Visitor& visitor) {
- // We loop through the first level of layers without traversing,
- // as we need to determine which layers belong to the requested display.
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (layer->getLayerStack() != layerStack) {
- continue;
- }
- // relative layers are traversed in Layer::traverseInZOrder
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (layer->isInternalDisplayOverlay()) {
- return;
- }
- if (!layer->isVisible()) {
- return;
- }
- if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
- return;
- }
-
- if (!excludeLayerIds.empty()) {
- auto p = sp<Layer>::fromExisting(layer);
- while (p != nullptr) {
- if (excludeLayerIds.count(p->sequence) != 0) {
- return;
- }
- p = p->getParent();
- }
- }
-
- visitor(layer);
- });
- }
-}
-
ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
@@ -8708,7 +7703,7 @@
const sp<DisplayDevice>& display,
const scheduler::RefreshRateSelector::PolicyVariant& policy) {
const auto displayId = display->getPhysicalId();
- ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
+ SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
Mutex::Autolock lock(mStateLock);
@@ -8737,13 +7732,9 @@
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
- // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
- // be depending in this callback.
- if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
- mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
- toggleKernelIdleTimer();
- } else {
- mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
+ if (const bool isPacesetter =
+ mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
+ mDisplayModeController.updateKernelIdleTimer(displayId);
}
auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -8767,10 +7758,7 @@
setDesiredMode({std::move(preferredMode), .emitEvent = true});
// Update the frameRateOverride list as the display render rate might have changed
- if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) {
- triggerOnFrameRateOverridesChanged();
- }
-
+ mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps);
return NO_ERROR;
}
@@ -8801,7 +7789,7 @@
status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
const gui::DisplayModeSpecs& specs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!displayToken) {
return BAD_VALUE;
@@ -8835,7 +7823,7 @@
status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
gui::DisplayModeSpecs* outSpecs) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (!displayToken || !outSpecs) {
return BAD_VALUE;
@@ -8862,17 +7850,12 @@
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
mNumLayers++;
- if (!layer->isRemovedFromCurrentState()) {
- mScheduler->registerLayer(layer);
- }
+ mScheduler->registerLayer(layer, scheduler::FrameRateCompatibility::Default);
}
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
mNumLayers--;
- removeHierarchyFromOffscreenLayers(layer);
- if (!layer->isRemovedFromCurrentState()) {
- mScheduler->deregisterLayer(layer);
- }
+ mScheduler->deregisterLayer(layer);
if (mTransactionTracing) {
mTransactionTracing->onLayerRemoved(layer->getSequence());
}
@@ -8883,24 +7866,6 @@
scheduleCommit(FrameHint::kActive);
}
-// WARNING: ONLY CALL THIS FROM LAYER DTOR
-// Here we add children in the current state to offscreen layers and remove the
-// layer itself from the offscreen layer list. Since
-// this is the dtor, it is safe to access the current state. This keeps us
-// from dangling children layers such that they are not reachable from the
-// Drawing state nor the offscreen layer list
-// See b/141111965
-void SurfaceFlinger::removeHierarchyFromOffscreenLayers(Layer* layer) {
- for (auto& child : layer->getCurrentChildren()) {
- mOffscreenLayers.emplace(child.get());
- }
- mOffscreenLayers.erase(layer);
-}
-
-void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
- mOffscreenLayers.erase(layer);
-}
-
status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ,
float lightRadius) {
@@ -8930,13 +7895,7 @@
}
status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
- PhysicalDisplayId displayId = [&]() {
- Mutex::Autolock lock(mStateLock);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
+ mScheduler->setGameModeFrameRateForUid(FrameRateOverride{uid, frameRate});
return NO_ERROR;
}
@@ -9049,51 +8008,6 @@
return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
-void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state, VsyncId vsyncId) {
- sp<Layer> layer = state.layer.promote();
- if (!layer) {
- ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get());
- return;
- }
- MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
-
- sp<Layer> parent;
- bool addToRoot = state.addToRoot;
- if (state.initialParent != nullptr) {
- parent = state.initialParent.promote();
- if (parent == nullptr) {
- ALOGD("Parent was destroyed soon after creation %p", state.initialParent.unsafe_get());
- addToRoot = false;
- }
- }
-
- if (parent == nullptr && addToRoot) {
- layer->setIsAtRoot(true);
- mCurrentState.layersSortedByZ.add(layer);
- } else if (parent == nullptr) {
- layer->onRemovedFromCurrentState();
- } else if (parent->isRemovedFromCurrentState()) {
- parent->addChild(layer);
- layer->onRemovedFromCurrentState();
- } else {
- parent->addChild(layer);
- }
-
- ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current);
- sp<const DisplayDevice> hintDisplay;
- // Find the display that includes the layer.
- for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == layerStack) {
- hintDisplay = display;
- break;
- }
- }
-
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
- }
-}
-
void SurfaceFlinger::sample() {
if (!mLumaSampling || !mRegionSamplingThread) {
return;
@@ -9127,7 +8041,7 @@
void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
const DisplayDevice& activeDisplay) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (inactiveDisplayPtr) {
inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
@@ -9136,8 +8050,6 @@
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- mScheduler->resetPhaseConfiguration(mDisplayModeController.getActiveMode(mActiveDisplayId).fps);
-
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
@@ -9261,170 +8173,52 @@
return nullptr;
}
-bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) {
- std::vector<MirrorDisplayState> mirrorDisplays;
- {
- std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
- mirrorDisplays = std::move(mMirrorDisplays);
- mMirrorDisplays.clear();
- if (mirrorDisplays.size() == 0) {
- return false;
- }
- }
-
- sp<IBinder> unused;
- for (const auto& mirrorDisplay : mirrorDisplays) {
- // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display
- // accidentally.
- sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle);
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(rootMirrorLayer);
- bool ret = rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
- if (idx >= 0 && ret) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(rootMirrorLayer);
- }
-
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (layer->getLayerStack() != mirrorDisplay.layerStack ||
- layer->isInternalDisplayOverlay()) {
- continue;
- }
-
- LayerCreationArgs mirrorArgs(this, mirrorDisplay.client, "MirrorLayerParent",
- ISurfaceComposerClient::eNoColorFill,
- gui::LayerMetadata());
- sp<Layer> childMirror;
- {
- Mutex::Autolock lock(mStateLock);
- createEffectLayer(mirrorArgs, &unused, &childMirror);
- MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock);
- childMirror->setClonedChild(layer->createClone());
- childMirror->reparent(mirrorDisplay.rootHandle);
- }
- // lock on mStateLock needs to be released before binder handle gets destroyed
- unused.clear();
- }
- }
- return true;
-}
-
-bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId,
- std::vector<LayerCreatedState>& createdLayers) {
- if (createdLayers.size() == 0) {
- return false;
- }
-
- Mutex::Autolock _l(mStateLock);
- for (const auto& createdLayer : createdLayers) {
- handleLayerCreatedLocked(createdLayer, vsyncId);
- }
- mLayersAdded = true;
- return mLayersAdded;
-}
-
-void SurfaceFlinger::updateLayerMetadataSnapshot() {
- LayerMetadata parentMetadata;
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- layer->updateMetadataSnapshot(parentMetadata);
- }
-
- std::unordered_set<Layer*> visited;
- mDrawingState.traverse([&visited](Layer* layer) {
- if (visited.find(layer) != visited.end()) {
- return;
- }
-
- // If the layer isRelativeOf, then either it's relative metadata will be set
- // recursively when updateRelativeMetadataSnapshot is called on its relative parent or
- // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure
- // that layers with deleted relative parents don't hold stale relativeLayerMetadata.
- if (layer->getDrawingState().isRelativeOf) {
- layer->editLayerSnapshot()->relativeLayerMetadata = {};
- return;
- }
-
- layer->updateRelativeMetadataSnapshot({}, visited);
- });
-}
-
void SurfaceFlinger::moveSnapshotsFromCompositionArgs(
compositionengine::CompositionRefreshArgs& refreshArgs,
const std::vector<std::pair<Layer*, LayerFE*>>& layers) {
- if (mLayerLifecycleManagerEnabled) {
- std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots =
- mLayerSnapshotBuilder.getSnapshots();
- for (auto [_, layerFE] : layers) {
- auto i = layerFE->mSnapshot->globalZ;
- snapshots[i] = std::move(layerFE->mSnapshot);
- }
- }
- if (!mLayerLifecycleManagerEnabled) {
- for (auto [layer, layerFE] : layers) {
- layer->updateLayerSnapshot(std::move(layerFE->mSnapshot));
- }
+ std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots =
+ mLayerSnapshotBuilder.getSnapshots();
+ for (auto [_, layerFE] : layers) {
+ auto i = layerFE->mSnapshot->globalZ;
+ snapshots[i] = std::move(layerFE->mSnapshot);
}
}
std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToCompositionArgs(
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly) {
std::vector<std::pair<Layer*, LayerFE*>> layers;
- if (mLayerLifecycleManagerEnabled) {
- nsecs_t currentTime = systemTime();
- mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
- kMainThreadContext) {
- if (cursorOnly &&
- snapshot->compositionType !=
- aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
- return;
- }
-
- if (!snapshot->hasSomethingToDraw()) {
- return;
- }
-
- auto it = mLegacyLayers.find(snapshot->sequence);
- LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
- auto& legacyLayer = it->second;
- sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
- snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
- layerFE->mSnapshot = std::move(snapshot);
- refreshArgs.layers.push_back(layerFE);
- layers.emplace_back(legacyLayer.get(), layerFE.get());
- });
- }
- if (!mLayerLifecycleManagerEnabled) {
- auto moveSnapshots = [&layers, &refreshArgs, cursorOnly](Layer* layer) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
+ nsecs_t currentTime = systemTime();
+ const bool needsMetadata = mCompositionEngine->getFeatureFlags().test(
+ compositionengine::Feature::kSnapshotLayerMetadata);
+ mLayerSnapshotBuilder.forEachSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
+ kMainThreadContext) {
if (cursorOnly &&
- layer->getLayerSnapshot()->compositionType !=
- aidl::android::hardware::graphics::composer3::Composition::CURSOR)
+ snapshot->compositionType !=
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
return;
- layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
- layerFE->mSnapshot = layer->stealLayerSnapshot();
+ }
+
+ if (!snapshot->hasSomethingToDraw()) {
+ return;
+ }
+
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ auto& legacyLayer = it->second;
+ sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+ snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
+ layerFE->mSnapshot = std::move(snapshot);
refreshArgs.layers.push_back(layerFE);
- layers.emplace_back(layer, layerFE.get());
- }
- };
-
- if (cursorOnly || !mVisibleRegionsDirty) {
- // for hot path avoid traversals by walking though the previous composition list
- for (sp<Layer> layer : mPreviouslyComposedLayers) {
- moveSnapshots(layer.get());
- }
- } else {
- mPreviouslyComposedLayers.clear();
- mDrawingState.traverseInZOrder(
- [&moveSnapshots](Layer* layer) { moveSnapshots(layer); });
- mPreviouslyComposedLayers.reserve(layers.size());
- for (auto [layer, _] : layers) {
- mPreviouslyComposedLayers.push_back(sp<Layer>::fromExisting(layer));
- }
- }
- }
-
+ layers.emplace_back(legacyLayer.get(), layerFE.get());
+ },
+ [needsMetadata](const frontend::LayerSnapshot& snapshot) {
+ return snapshot.isVisible ||
+ (needsMetadata &&
+ snapshot.changes.test(frontend::RequestedLayerState::Changes::Metadata));
+ });
return layers;
}
@@ -9551,33 +8345,6 @@
};
}
-frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
- frontend::Update update;
- ATRACE_NAME("TransactionHandler:flushTransactions");
- // Locking:
- // 1. to prevent onHandleDestroyed from being called while the state lock is held,
- // we must keep a copy of the transactions (specifically the composer
- // states) around outside the scope of the lock.
- // 2. Transactions and created layers do not share a lock. To prevent applying
- // transactions with layers still in the createdLayer queue, flush the transactions
- // before committing the created layers.
- mTransactionHandler.collectTransactions();
- update.transactions = mTransactionHandler.flushTransactions();
- {
- // TODO(b/238781169) lockless queue this and keep order.
- std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- update.layerCreatedStates = std::move(mCreatedLayers);
- mCreatedLayers.clear();
- update.newLayers = std::move(mNewLayers);
- mNewLayers.clear();
- update.layerCreationArgs = std::move(mNewLayerArgs);
- mNewLayerArgs.clear();
- update.destroyedHandles = std::move(mDestroyedHandles);
- mDestroyedHandles.clear();
- }
- return update;
-}
-
void SurfaceFlinger::doActiveLayersTracingIfNeeded(bool isCompositionComputed,
bool visibleRegionDirty, TimePoint time,
VsyncId vsyncId) {
@@ -9599,7 +8366,7 @@
perfetto::protos::LayersSnapshotProto SurfaceFlinger::takeLayersSnapshotProto(
uint32_t traceFlags, TimePoint time, VsyncId vsyncId, bool visibleRegionDirty) {
- ATRACE_CALL();
+ SFTRACE_CALL();
perfetto::protos::LayersSnapshotProto snapshot;
snapshot.set_elapsed_realtime_nanos(time.ns());
snapshot.set_vsync_id(ftl::to_underlying(vsyncId));
@@ -9608,9 +8375,6 @@
0);
auto layers = dumpDrawingStateProto(traceFlags);
- if (traceFlags & LayerTracing::Flag::TRACE_EXTRA) {
- dumpOffscreenLayersProto(layers);
- }
*snapshot.mutable_layers() = std::move(layers);
if (traceFlags & LayerTracing::Flag::TRACE_HWC) {
@@ -10489,6 +9253,28 @@
return ::android::binder::Status::ok();
}
+binder::Status SurfaceComposerAIDL::addJankListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IJankListener>& listener) {
+ sp<Layer> layer = LayerHandle::getLayer(layerHandle);
+ if (layer == nullptr) {
+ return binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER);
+ }
+ JankTracker::addJankListener(layer->sequence, IInterface::asBinder(listener));
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::flushJankData(int32_t layerId) {
+ JankTracker::flushJankData(layerId);
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::removeJankListener(int32_t layerId,
+ const sp<gui::IJankListener>& listener,
+ int64_t afterVsync) {
+ JankTracker::removeJankListener(layerId, IInterface::asBinder(listener), afterVsync);
+ return binder::Status::ok();
+}
+
status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index da797f8..0808c12 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -27,7 +27,9 @@
#include <android/gui/BnSurfaceComposer.h>
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
+#include <android/gui/IJankListener.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <common/trace.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <ftl/algorithm.h>
@@ -52,7 +54,6 @@
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
-#include <utils/Trace.h>
#include <utils/threads.h>
#include <compositionengine/OutputColorSetting.h>
@@ -134,6 +135,7 @@
class ScreenCapturer;
class WindowInfosListenerInvoker;
+using ::aidl::android::hardware::drm::HdcpLevels;
using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using frontend::TransactionHandler;
@@ -272,7 +274,7 @@
enum class FrameHint { kNone, kActive };
// Schedule commit of transactions on the main thread ahead of the next VSYNC.
- void scheduleCommit(FrameHint);
+ void scheduleCommit(FrameHint, Duration workDurationSlack = Duration::fromNs(0));
// As above, but also force composite regardless if transactions were committed.
void scheduleComposite(FrameHint);
// As above, but also force dirty geometry to repaint.
@@ -291,16 +293,11 @@
void onLayerDestroyed(Layer*);
void onLayerUpdate();
- void removeHierarchyFromOffscreenLayers(Layer* layer);
- void removeFromOffscreenLayers(Layer* layer);
-
// Called when all clients have released all their references to
// this layer. The layer may still be kept alive by its parents but
// the client can no longer modify this layer directly.
void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
- std::vector<Layer*> mLayerMirrorRoots;
-
TransactionCallbackInvoker& getTransactionCallbackInvoker() {
return mTransactionCallbackInvoker;
}
@@ -312,7 +309,6 @@
// Disables expensive rendering for all displays
// This is scheduled on the main thread
void disableExpensiveRendering();
- FloatRect getMaxDisplayBounds();
// If set, composition engine tries to predict the composition strategy provided by HWC
// based on the previous frame. If the strategy can be predicted, gpu composition will
@@ -392,11 +388,10 @@
class State {
public:
- explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {}
+ explicit State(LayerVector::StateSet set) : stateSet(set) {}
State& operator=(const State& other) {
// We explicitly don't copy stateSet so that, e.g., mDrawingState
// always uses the Drawing StateSet.
- layersSortedByZ = other.layersSortedByZ;
displays = other.displays;
colorMatrixChanged = other.colorMatrixChanged;
if (colorMatrixChanged) {
@@ -408,7 +403,6 @@
}
const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid;
- LayerVector layersSortedByZ;
// TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap.
DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays;
@@ -428,10 +422,6 @@
mat4 colorMatrix;
ShadowSettings globalShadowSettings;
-
- void traverse(const LayerVector::Visitor& visitor) const;
- void traverseInZOrder(const LayerVector::Visitor& visitor) const;
- void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
// Keeps track of pending buffers per layer handle in the transaction queue or current/drawing
@@ -449,7 +439,7 @@
if (it != mCounterByLayerHandle.end()) {
auto [name, pendingBuffers] = it->second;
int32_t count = ++(*pendingBuffers);
- ATRACE_INT(name.c_str(), count);
+ SFTRACE_INT(name.c_str(), count);
} else {
ALOGW("Handle not found! %p", layerHandle);
}
@@ -559,7 +549,7 @@
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -682,6 +672,7 @@
void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
void onComposerHalVsyncIdle(hal::HWDisplayId) override;
void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
+ void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) override;
// ICompositor overrides:
void configure() override REQUIRES(kMainThreadContext);
@@ -697,7 +688,6 @@
void requestHardwareVsync(PhysicalDisplayId, bool) override;
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
- void triggerOnFrameRateOverridesChanged() override;
void onChoreographerAttached() override;
void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
Fps renderRate) override;
@@ -708,22 +698,13 @@
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
- // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
- void toggleKernelIdleTimer() REQUIRES(mStateLock);
-
using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
// Get the controller and timeout that will help decide how the kernel idle timer will be
// configured and what value to use as the timeout.
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
getKernelIdleTimerProperties(PhysicalDisplayId) REQUIRES(mStateLock);
- // Updates the kernel idle timer either through HWC or through sysprop
- // depending on which controller is provided
- void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController,
- PhysicalDisplayId) REQUIRES(mStateLock);
- // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
- // make calls to sys prop each time.
- bool mKernelIdleTimerEnabled = false;
+
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
// Show render rate with refresh rate overlay
@@ -763,17 +744,11 @@
const scheduler::RefreshRateSelector&)
REQUIRES(mStateLock, kMainThreadContext);
- void commitTransactionsLegacy() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
void commitTransactions() REQUIRES(kMainThreadContext, mStateLock);
void commitTransactionsLocked(uint32_t transactionFlags)
REQUIRES(mStateLock, kMainThreadContext);
void doCommitTransactions() REQUIRES(mStateLock);
- // Returns whether a new buffer has been latched.
- bool latchBuffers();
-
- void updateLayerGeometry();
- void updateLayerMetadataSnapshot();
std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly)
REQUIRES(kMainThreadContext);
@@ -781,13 +756,9 @@
const std::vector<std::pair<Layer*, LayerFE*>>& layers)
REQUIRES(kMainThreadContext);
// Return true if we must composite this frame
- bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
- bool& out) REQUIRES(kMainThreadContext);
- // Return true if we must composite this frame
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
void updateLayerHistory(nsecs_t now) REQUIRES(kMainThreadContext);
- frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) REQUIRES(kMainThreadContext);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -826,16 +797,10 @@
TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
- TransactionHandler::TransactionReadiness transactionReadyBufferCheckLegacy(
- const TransactionHandler::TransactionFlushState& flushState)
- REQUIRES(kMainThreadContext);
TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
- uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint64_t transactionId) REQUIRES(mStateLock);
uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint64_t transactionId)
@@ -850,8 +815,6 @@
// Clears and returns the masked bits.
uint32_t clearTransactionFlags(uint32_t mask);
- void commitOffscreenLayers();
-
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
bool applyTransactionsLocked(std::vector<TransactionState>& transactions)
@@ -878,16 +841,11 @@
status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
gui::CreateSurfaceResult& outResult);
- void markLayerPendingRemovalLocked(const sp<Layer>& layer) REQUIRES(mStateLock);
-
// add a layer to SurfaceFlinger
status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parentLayer,
uint32_t* outTransformHint);
- // Traverse through all the layers and compute and cache its bounds.
- void computeLayerBounds();
-
// Creates a promise for a future release fence for a layer. This allows for
// the layer to keep track of when its buffer can be released.
void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack);
@@ -897,13 +855,13 @@
using OutputCompositionState = compositionengine::impl::OutputCompositionState;
- std::optional<OutputCompositionState> getDisplayAndLayerSnapshotsFromMainThread(
+ std::optional<OutputCompositionState> getSnapshotsFromMainThread(
RenderAreaBuilderVariant& renderAreaBuilder,
GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs);
void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
- bool grayscale, const sp<IScreenCaptureListener>&);
+ bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&);
std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
@@ -917,29 +875,24 @@
ftl::SharedFuture<FenceResult> captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>& captureListener,
std::optional<OutputCompositionState>& displayState,
std::vector<sp<LayerFE>>& layerFEs);
ftl::SharedFuture<FenceResult> captureScreenshotLegacy(
RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- std::unique_ptr<const RenderArea>,
- const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, bool isProtected, ScreenCaptureResults&,
- std::optional<OutputCompositionState>& displayState,
+ const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
+ bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
+ ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState,
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
std::vector<sp<LayerFE>>& layerFEs);
- // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
- // matching ownerUid
- void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid,
- std::unordered_set<uint32_t> excludeLayerIds,
- const LayerVector::Visitor&);
-
void readPersistentProperties();
uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
@@ -988,18 +941,12 @@
return nullptr;
}
- // Returns the primary display or (for foldables) the active display, assuming that the inner
- // and outer displays have mutually exclusive power states.
+ // Returns the primary display or (for foldables) the active display.
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
}
sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
- return display;
- }
- // The active display is outdated, so fall back to the primary display.
- mActiveDisplayId = getPrimaryDisplayIdLocked();
return getDisplayDeviceLocked(mActiveDisplayId);
}
@@ -1099,13 +1046,6 @@
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&);
-
- /*
- * VSYNC
- */
- nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
-
/*
* Display identification
*/
@@ -1156,7 +1096,6 @@
void dumpAll(const DumpArgs& args, const std::string& compositionLayers,
std::string& result) const EXCLUDES(mStateLock);
void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext);
- void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
void listLayers(std::string& result) const REQUIRES(kMainThreadContext);
@@ -1182,8 +1121,6 @@
perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const
REQUIRES(kMainThreadContext);
- void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto> dumpDisplayProto() const;
void doActiveLayersTracingIfNeeded(bool isCompositionComputed, bool visibleRegionDirty,
TimePoint, VsyncId) REQUIRES(kMainThreadContext);
@@ -1195,7 +1132,6 @@
void dumpHwc(std::string& result) const;
perfetto::protos::LayersProto dumpProtoFromMainThread(
uint32_t traceFlags = LayerTracing::TRACE_ALL) EXCLUDES(mStateLock);
- void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
status_t doDump(int fd, const DumpArgs& args, bool asProto);
@@ -1256,7 +1192,6 @@
State mCurrentState{LayerVector::StateSet::Current};
std::atomic<int32_t> mTransactionFlags = 0;
std::atomic<uint32_t> mUniqueTransactionId = 1;
- SortedVector<sp<Layer>> mLayersPendingRemoval;
// Buffers that have been discarded by clients and need to be evicted from per-layer caches so
// the graphics memory can be immediately freed.
@@ -1296,21 +1231,22 @@
// TODO: Also move visibleRegions over to a boolean system.
bool mUpdateInputInfo = false;
bool mSomeChildrenChanged;
- bool mForceTransactionDisplayChange = false;
bool mUpdateAttachedChoreographer = false;
- // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
- bool mLayerMetadataSnapshotNeeded = false;
+ struct LayerIntHash {
+ size_t operator()(const std::pair<sp<Layer>, gui::GameMode>& k) const {
+ return std::hash<Layer*>()(k.first.get()) ^
+ std::hash<int32_t>()(static_cast<int32_t>(k.second));
+ }
+ };
// TODO(b/238781169) validate these on composition
// Tracks layers that have pending frames which are candidates for being
// latched.
- std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
+ std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames;
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames;
- // Tracks layers that need to update a display's dirty region.
- std::vector<sp<Layer>> mLayersPendingRefresh;
// Sorted list of layers that were composed during previous frame. This is used to
// avoid an expensive traversal of the layer hierarchy when there are no
// visible region changes. Because this is a list of strong pointers, this will
@@ -1325,7 +1261,6 @@
};
bool mIsHdcpViaNegVsync = false;
- bool mIsHotplugErrViaNegVsync = false;
std::mutex mHotplugMutex;
std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex);
@@ -1338,7 +1273,7 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
- // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+ // The inner or outer display for foldables, while unfolded or folded, respectively.
std::atomic<PhysicalDisplayId> mActiveDisplayId;
display::DisplayModeController mDisplayModeController;
@@ -1439,38 +1374,11 @@
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
- // A set of layers that have no parent so they are not drawn on screen.
- // Should only be accessed by the main thread.
- // The Layer pointer is removed from the set when the destructor is called so there shouldn't
- // be any issues with a raw pointer referencing an invalid object.
- std::unordered_set<Layer*> mOffscreenLayers;
-
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
- mutable std::mutex mCreatedLayersLock;
-
- // A temporay pool that store the created layers and will be added to current state in main
- // thread.
- std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock);
- bool commitCreatedLayers(VsyncId, std::vector<LayerCreatedState>& createdLayers);
- void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock);
-
- mutable std::mutex mMirrorDisplayLock;
- struct MirrorDisplayState {
- MirrorDisplayState(ui::LayerStack layerStack, sp<IBinder>& rootHandle,
- const sp<Client>& client)
- : layerStack(layerStack), rootHandle(rootHandle), client(client) {}
-
- ui::LayerStack layerStack;
- sp<IBinder> rootHandle;
- const sp<Client> client;
- };
- std::vector<MirrorDisplayState> mMirrorDisplays GUARDED_BY(mMirrorDisplayLock);
- bool commitMirrorDisplays(VsyncId);
-
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
// Must only be accessed on the main thread.
@@ -1504,8 +1412,6 @@
}
bool mPowerHintSessionEnabled;
-
- bool mLayerLifecycleManagerEnabled = false;
// Whether a display should be turned on when initialized
bool mSkipPowerOnForQuiescent;
@@ -1513,6 +1419,8 @@
frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext);
frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext);
+ mutable std::mutex mCreatedLayersLock;
+ std::vector<sp<Layer>> mCreatedLayers GUARDED_BY(mCreatedLayersLock);
std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles GUARDED_BY(mCreatedLayersLock);
std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers
GUARDED_BY(mCreatedLayersLock);
@@ -1694,6 +1602,11 @@
int pid, std::optional<gui::StalledTransactionInfo>* outInfo) override;
binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
binder::Status notifyShutdown() override;
+ binder::Status addJankListener(const sp<IBinder>& layer,
+ const sp<gui::IJankListener>& listener) override;
+ binder::Status flushJankData(int32_t layerId) override;
+ binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
+ int64_t afterVsync) override;
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index a631074..a6a0152 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -20,10 +20,12 @@
"libtimestats_atoms_proto",
"libui",
"libutils",
+ "libtracing_perfetto",
],
static_libs: [
"libtimestats_proto",
+ "libsurfaceflinger_common",
],
export_static_lib_headers: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 368cb41..c60ded6 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -19,11 +19,11 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <log/log.h>
#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
#include <utils/String8.h>
#include <utils/Timers.h>
-#include <utils/Trace.h>
#include <algorithm>
#include <chrono>
@@ -271,7 +271,7 @@
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::unordered_map<std::string, int32_t> argsMap;
for (size_t index = 0; index < args.size(); ++index) {
@@ -304,7 +304,7 @@
}
std::string TimeStats::miniDump() {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::string result = "TimeStats miniDump:\n";
std::lock_guard<std::mutex> lock(mMutex);
@@ -318,7 +318,7 @@
void TimeStats::incrementTotalFrames() {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
mTimeStats.totalFramesLegacy++;
@@ -327,7 +327,7 @@
void TimeStats::incrementMissedFrames() {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
mTimeStats.missedFramesLegacy++;
@@ -338,7 +338,7 @@
return;
}
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
if (record.changed) mTimeStats.compositionStrategyChangesLegacy++;
@@ -351,7 +351,7 @@
void TimeStats::incrementRefreshRateSwitches() {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
mTimeStats.refreshRateSwitchesLegacy++;
@@ -445,7 +445,7 @@
std::optional<Fps> renderRate,
SetFrameRateVote frameRateVote,
GameMode gameMode) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
@@ -568,7 +568,7 @@
uid_t uid, nsecs_t postTime, GameMode gameMode) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerId, frameNumber, layerName.c_str(),
postTime);
@@ -612,7 +612,7 @@
void TimeStats::setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerId, frameNumber, latchTime);
std::lock_guard<std::mutex> lock(mMutex);
@@ -630,7 +630,7 @@
void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-LatchSkipped-Reason[%d]", layerId,
static_cast<std::underlying_type<LatchSkipReason>::type>(reason));
@@ -648,7 +648,7 @@
void TimeStats::incrementBadDesiredPresent(int32_t layerId) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-BadDesiredPresent", layerId);
std::lock_guard<std::mutex> lock(mMutex);
@@ -660,7 +660,7 @@
void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerId, frameNumber, desiredTime);
std::lock_guard<std::mutex> lock(mMutex);
@@ -678,7 +678,7 @@
void TimeStats::setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerId, frameNumber, acquireTime);
std::lock_guard<std::mutex> lock(mMutex);
@@ -697,7 +697,7 @@
const std::shared_ptr<FenceTime>& acquireFence) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerId, frameNumber,
acquireFence->getSignalTime());
@@ -718,7 +718,7 @@
SetFrameRateVote frameRateVote, GameMode gameMode) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerId, frameNumber, presentTime);
std::lock_guard<std::mutex> lock(mMutex);
@@ -744,7 +744,7 @@
SetFrameRateVote frameRateVote, GameMode gameMode) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerId, frameNumber,
presentFence->getSignalTime());
@@ -805,7 +805,7 @@
void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
// Only update layer stats if we're already tracking the layer in TimeStats.
@@ -861,7 +861,7 @@
}
void TimeStats::onDestroy(int32_t layerId) {
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-onDestroy", layerId);
std::lock_guard<std::mutex> lock(mMutex);
mTimeStatsTracker.erase(layerId);
@@ -870,7 +870,7 @@
void TimeStats::removeTimeRecord(int32_t layerId, uint64_t frameNumber) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerId, frameNumber);
std::lock_guard<std::mutex> lock(mMutex);
@@ -935,7 +935,7 @@
}
void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
- ATRACE_CALL();
+ SFTRACE_CALL();
while (!mGlobalRecord.presentFences.empty()) {
const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime();
@@ -992,7 +992,7 @@
void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
if (presentFence == nullptr || !presentFence->isValid()) {
mGlobalRecord.prevPresentTime = 0;
@@ -1022,7 +1022,7 @@
void TimeStats::enable() {
if (mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
mEnabled.store(true);
@@ -1034,7 +1034,7 @@
void TimeStats::disable() {
if (!mEnabled.load()) return;
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
flushPowerTimeLocked();
@@ -1051,7 +1051,7 @@
}
void TimeStats::clearGlobalLocked() {
- ATRACE_CALL();
+ SFTRACE_CALL();
mTimeStats.statsStartLegacy = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
mTimeStats.statsEndLegacy = 0;
@@ -1078,7 +1078,7 @@
}
void TimeStats::clearLayersLocked() {
- ATRACE_CALL();
+ SFTRACE_CALL();
mTimeStatsTracker.clear();
@@ -1093,7 +1093,7 @@
}
void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result) {
- ATRACE_CALL();
+ SFTRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStartLegacy == 0) {
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 1adc3a5..51f33a6 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -21,8 +21,8 @@
#include <functional>
#include <string>
+#include <common/trace.h>
#include <cutils/compiler.h>
-#include <utils/Trace.h>
namespace android {
@@ -79,7 +79,7 @@
private:
void trace() {
- if (CC_LIKELY(!ATRACE_ENABLED())) {
+ if (CC_LIKELY(!SFTRACE_ENABLED())) {
return;
}
@@ -88,13 +88,13 @@
}
if (!signbit(mData)) {
- ATRACE_INT64(mName.c_str(), to_int64(mData));
+ SFTRACE_INT64(mName.c_str(), to_int64(mData));
if (mHasGoneNegative) {
- ATRACE_INT64(mNameNegative.c_str(), 0);
+ SFTRACE_INT64(mNameNegative.c_str(), 0);
}
} else {
- ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData));
- ATRACE_INT64(mName.c_str(), 0);
+ SFTRACE_INT64(mNameNegative.c_str(), -to_int64(mData));
+ SFTRACE_INT64(mName.c_str(), 0);
}
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 41bcdf0..d40b888 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -24,10 +24,10 @@
#include "Tracing/tools/LayerTraceGenerator.h"
#include "TransactionTracing.h"
+#include <common/trace.h>
#include <log/log.h>
#include <perfetto/tracing.h>
#include <utils/Timers.h>
-#include <utils/Trace.h>
namespace android {
@@ -134,7 +134,7 @@
void LayerTracing::addProtoSnapshotToOstream(perfetto::protos::LayersSnapshotProto&& snapshot,
Mode mode) {
- ATRACE_CALL();
+ SFTRACE_CALL();
if (mOutStream) {
writeSnapshotToStream(std::move(snapshot));
} else {
diff --git a/services/surfaceflinger/Tracing/TransactionRingBuffer.h b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
index 7d1d3fd..2b66391 100644
--- a/services/surfaceflinger/Tracing/TransactionRingBuffer.h
+++ b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
@@ -19,13 +19,12 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <common/trace.h>
#include <log/log.h>
#include <utils/Errors.h>
#include <utils/Timers.h>
-#include <utils/Trace.h>
#include <chrono>
#include <fstream>
-#include <queue>
namespace android {
@@ -57,7 +56,7 @@
}
status_t appendToStream(FileProto& fileProto, std::ofstream& out) {
- ATRACE_CALL();
+ SFTRACE_CALL();
writeToProto(fileProto);
std::string output;
if (!fileProto.SerializeToString(&output)) {
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index 8afca41..63c1b37 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -28,6 +28,7 @@
"libsurfaceflinger_mocks_defaults",
"librenderengine_deps",
"surfaceflinger_defaults",
+ "libsurfaceflinger_common_deps",
],
srcs: [
":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 617ea2c..1dba175 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -162,7 +162,10 @@
auto layersProto =
LayerProtoFromSnapshotGenerator(snapshotBuilder, displayInfos, {}, traceFlags)
- .generate(hierarchyBuilder.getHierarchy());
+ .with(hierarchyBuilder.getHierarchy())
+ .withOffscreenLayers(hierarchyBuilder.getOffscreenHierarchy())
+ .generate();
+
auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
if (!onlyLastEntry || (i == traceFile.entry_size() - 1)) {
perfetto::protos::LayersSnapshotProto snapshotProto{};
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 222ae30..c6856ae 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -27,10 +27,9 @@
#include "BackgroundExecutor.h"
#include "Utils/FenceUtils.h"
-#include <cinttypes>
-
#include <binder/IInterface.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <utils/RefBase.h>
namespace android {
@@ -64,13 +63,12 @@
if (handles.empty()) {
return NO_ERROR;
}
- const std::vector<JankData>& jankData = std::vector<JankData>();
for (const auto& handle : handles) {
if (!containsOnCommitCallbacks(handle->callbackIds)) {
outRemainingHandles.push_back(handle);
continue;
}
- status_t err = addCallbackHandle(handle, jankData);
+ status_t err = addCallbackHandle(handle);
if (err != NO_ERROR) {
return err;
}
@@ -80,12 +78,12 @@
}
status_t TransactionCallbackInvoker::addCallbackHandles(
- const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
+ const std::deque<sp<CallbackHandle>>& handles) {
if (handles.empty()) {
return NO_ERROR;
}
for (const auto& handle : handles) {
- status_t err = addCallbackHandle(handle, jankData);
+ status_t err = addCallbackHandle(handle);
if (err != NO_ERROR) {
return err;
}
@@ -111,8 +109,7 @@
return NO_ERROR;
}
-status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData) {
+status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle) {
// If we can't find the transaction stats something has gone wrong. The client should call
// startRegistration before trying to add a callback handle.
TransactionStats* transactionStats;
@@ -151,8 +148,14 @@
handle->previousReleaseFence,
handle->transformHint,
handle->currentMaxAcquiredBufferCount,
- eventStats, jankData,
- handle->previousReleaseCallbackId);
+ eventStats, handle->previousReleaseCallbackId);
+ if (handle->bufferReleaseChannel &&
+ handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
+ mBufferReleases.emplace_back(handle->bufferReleaseChannel,
+ handle->previousReleaseCallbackId,
+ handle->previousReleaseFence,
+ handle->currentMaxAcquiredBufferCount);
+ }
}
return NO_ERROR;
}
@@ -162,9 +165,15 @@
}
void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
+ for (const auto& bufferRelease : mBufferReleases) {
+ bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
+ bufferRelease.currentMaxAcquiredBufferCount);
+ }
+ mBufferReleases.clear();
+
// For each listener
auto completedTransactionsItr = mCompletedTransactions.begin();
- BackgroundExecutor::Callbacks callbacks;
+ ftl::SmallVector<ListenerStats, 10> listenerStatsToSend;
while (completedTransactionsItr != mCompletedTransactions.end()) {
auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
ListenerStats listenerStats;
@@ -199,10 +208,7 @@
// keep it as an IBinder due to consistency reasons: if we
// interface_cast at the IPC boundary when reading a Parcel,
// we get pointers that compare unequal in the SF process.
- callbacks.emplace_back([stats = std::move(listenerStats)]() {
- interface_cast<ITransactionCompletedListener>(stats.listener)
- ->onTransactionCompleted(stats);
- });
+ listenerStatsToSend.emplace_back(std::move(listenerStats));
}
}
completedTransactionsItr++;
@@ -212,7 +218,14 @@
mPresentFence.clear();
}
- BackgroundExecutor::getInstance().sendCallbacks(std::move(callbacks));
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[listenerStatsToSend = std::move(listenerStatsToSend)]() {
+ SFTRACE_NAME("TransactionCallbackInvoker::sendCallbacks");
+ for (auto& stats : listenerStatsToSend) {
+ interface_cast<ITransactionCompletedListener>(stats.listener)
+ ->onTransactionCompleted(stats);
+ }
+ }});
}
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index cb7150b..14a7487 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -16,18 +16,14 @@
#pragma once
-#include <condition_variable>
#include <deque>
-#include <mutex>
#include <optional>
-#include <queue>
-#include <thread>
#include <unordered_map>
-#include <unordered_set>
#include <android-base/thread_annotations.h>
#include <binder/IBinder.h>
#include <ftl/future.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/ITransactionCompletedListener.h>
#include <ui/Fence.h>
#include <ui/FenceResult.h>
@@ -59,12 +55,12 @@
uint64_t frameNumber = 0;
uint64_t previousFrameNumber = 0;
ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
};
class TransactionCallbackInvoker {
public:
- status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
- const std::vector<JankData>& jankData);
+ status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
std::deque<sp<CallbackHandle>>& outRemainingHandles);
@@ -77,9 +73,7 @@
mCompletedTransactions.clear();
}
- status_t addCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData);
-
+ status_t addCallbackHandle(const sp<CallbackHandle>& handle);
private:
status_t findOrCreateTransactionStats(const sp<IBinder>& listener,
@@ -89,6 +83,14 @@
std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
mCompletedTransactions;
+ struct BufferRelease {
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel;
+ ReleaseCallbackId callbackId;
+ sp<Fence> fence;
+ uint32_t currentMaxAcquiredBufferCount;
+ };
+ std::vector<BufferRelease> mBufferReleases;
+
sp<Fence> mPresentFence;
};
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h
index 198e7b2..215472b 100644
--- a/services/surfaceflinger/Utils/RingBuffer.h
+++ b/services/surfaceflinger/Utils/RingBuffer.h
@@ -43,8 +43,10 @@
}
T& front() { return (*this)[0]; }
+ const T& front() const { return (*this)[0]; }
T& back() { return (*this)[size() - 1]; }
+ const T& back() const { return (*this)[size() - 1]; }
T& operator[](size_t index) {
return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index effbfdb..895e054 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -17,8 +17,8 @@
#include <android/gui/BnWindowInfosPublisher.h>
#include <android/gui/IWindowInfosPublisher.h>
#include <android/gui/WindowInfosListenerInfo.h>
+#include <common/trace.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/TraceUtils.h>
#include <gui/WindowInfosUpdate.h>
#include <scheduler/Time.h>
@@ -42,7 +42,7 @@
BackgroundExecutor::getInstance().sendCallbacks(
{[this, listener = std::move(listener), listenerId]() {
- ATRACE_NAME("WindowInfosListenerInvoker::addWindowInfosListener");
+ SFTRACE_NAME("WindowInfosListenerInvoker::addWindowInfosListener");
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
mWindowInfosListeners.try_emplace(asBinder,
@@ -53,7 +53,7 @@
void WindowInfosListenerInvoker::removeWindowInfosListener(
const sp<IWindowInfosListener>& listener) {
BackgroundExecutor::getInstance().sendCallbacks({[this, listener]() {
- ATRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener");
+ SFTRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener");
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
eraseListenerAndAckMessages(asBinder);
@@ -62,7 +62,7 @@
void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
BackgroundExecutor::getInstance().sendCallbacks({[this, who]() {
- ATRACE_NAME("WindowInfosListenerInvoker::binderDied");
+ SFTRACE_NAME("WindowInfosListenerInvoker::binderDied");
eraseListenerAndAckMessages(who);
}});
}
@@ -146,7 +146,7 @@
WindowInfosListenerInvoker::DebugInfo WindowInfosListenerInvoker::getDebugInfo() {
DebugInfo result;
BackgroundExecutor::getInstance().sendCallbacks({[&, this]() {
- ATRACE_NAME("WindowInfosListenerInvoker::getDebugInfo");
+ SFTRACE_NAME("WindowInfosListenerInvoker::getDebugInfo");
updateMaxSendDelay();
result = mDebugInfo;
result.pendingMessageCount = mUnackedState.size();
@@ -169,7 +169,7 @@
binder::Status WindowInfosListenerInvoker::ackWindowInfosReceived(int64_t vsyncId,
int64_t listenerId) {
BackgroundExecutor::getInstance().sendCallbacks({[this, vsyncId, listenerId]() {
- ATRACE_NAME("WindowInfosListenerInvoker::ackWindowInfosReceived");
+ SFTRACE_NAME("WindowInfosListenerInvoker::ackWindowInfosReceived");
auto it = mUnackedState.find(vsyncId);
if (it == mUnackedState.end()) {
return;
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index bcf1886..f9c99bf 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -18,6 +18,7 @@
"libSurfaceFlingerProp",
"server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libtracing_perfetto",
],
static_libs: [
"librenderengine_includes",
@@ -27,6 +28,7 @@
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
+ export_shared_lib_headers: ["libtracing_perfetto"],
}
cc_library_static {
@@ -60,6 +62,7 @@
shared_libs: [
"server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libtracing_perfetto",
],
static_libs: [
"libsurfaceflinger_common",
@@ -75,6 +78,7 @@
shared_libs: [
"server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libtracing_perfetto",
],
static_libs: [
"libsurfaceflinger_common_test",
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 2e3273c..12d6138 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -136,23 +136,27 @@
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
+ DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame);
DUMP_READ_ONLY_FLAG(restore_blur_step);
DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
DUMP_READ_ONLY_FLAG(protected_if_client);
DUMP_READ_ONLY_FLAG(ce_fence_promise);
DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
DUMP_READ_ONLY_FLAG(graphite_renderengine);
+ DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts);
DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed);
DUMP_READ_ONLY_FLAG(deprecate_vsync_sf);
DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter);
DUMP_READ_ONLY_FLAG(detached_mirror);
DUMP_READ_ONLY_FLAG(commit_not_composited);
+ DUMP_READ_ONLY_FLAG(correct_dpi_with_display_size);
DUMP_READ_ONLY_FLAG(local_tonemap_screenshots);
DUMP_READ_ONLY_FLAG(override_trusted_overlay);
DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache);
DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine);
DUMP_READ_ONLY_FLAG(single_hop_screenshot);
DUMP_READ_ONLY_FLAG(trace_frame_rate_override);
+ DUMP_READ_ONLY_FLAG(true_hdr_screenshots);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
@@ -242,18 +246,22 @@
FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
+FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
+FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "")
FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, "");
FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, "");
FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, "");
FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
+FLAG_MANAGER_READ_ONLY_FLAG(correct_dpi_with_display_size, "");
FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
+FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index ab7a474..a1be194 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -73,6 +73,7 @@
bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
bool vrr_bugfix_24q4() const;
+ bool vrr_bugfix_dropped_frame() const;
bool renderable_buffer_usage() const;
bool restore_blur_step() const;
bool dont_skip_on_early_ro() const;
@@ -80,17 +81,20 @@
bool ce_fence_promise() const;
bool idle_screen_refresh_rate_timeout() const;
bool graphite_renderengine() const;
+ bool filter_frames_before_trace_starts() const;
bool latch_unsignaled_with_auto_refresh_changed() const;
bool deprecate_vsync_sf() const;
bool allow_n_vsyncs_in_targeter() const;
bool detached_mirror() const;
bool commit_not_composited() const;
+ bool correct_dpi_with_display_size() const;
bool local_tonemap_screenshots() const;
bool override_trusted_overlay() const;
bool flush_buffer_slots_to_uncache() const;
bool force_compile_graphite_renderengine() const;
bool single_hop_screenshot() const;
bool trace_frame_rate_override() const;
+ bool true_hdr_screenshots() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
new file mode 100644
index 0000000..dc5716b
--- /dev/null
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -0,0 +1,90 @@
+
+/*
+ * 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
+
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#endif
+
+#include <cutils/trace.h>
+#include <tracing_perfetto.h>
+
+// prevent using atrace directly, calls should go through tracing_perfetto lib
+#undef ATRACE_ENABLED
+#undef ATRACE_BEGIN
+#undef ATRACE_END
+#undef ATRACE_ASYNC_BEGIN
+#undef ATRACE_ASYNC_END
+#undef ATRACE_ASYNC_FOR_TRACK_BEGIN
+#undef ATRACE_ASYNC_FOR_TRACK_END
+#undef ATRACE_INSTANT
+#undef ATRACE_INSTANT_FOR_TRACK
+#undef ATRACE_INT
+#undef ATRACE_INT64
+#undef ATRACE_CALL
+#undef ATRACE_NAME
+#undef ATRACE_FORMAT
+#undef ATRACE_FORMAT_INSTANT
+
+#define SFTRACE_ENABLED() ::tracing_perfetto::isTagEnabled(ATRACE_TAG)
+#define SFTRACE_BEGIN(name) ::tracing_perfetto::traceBegin(ATRACE_TAG, name)
+#define SFTRACE_END() ::tracing_perfetto::traceEnd(ATRACE_TAG)
+#define SFTRACE_ASYNC_BEGIN(name, cookie) \
+ ::tracing_perfetto::traceAsyncBegin(ATRACE_TAG, name, cookie)
+#define SFTRACE_ASYNC_END(name, cookie) ::tracing_perfetto::traceAsyncEnd(ATRACE_TAG, name, cookie)
+#define SFTRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
+ ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, name, track_name, cookie)
+#define SFTRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
+ ::tracing_perfetto::traceAsyncEndForTrack(ATRACE_TAG, track_name, cookie)
+#define SFTRACE_INSTANT(name) ::tracing_perfetto::traceInstant(ATRACE_TAG, name)
+#define SFTRACE_FORMAT_INSTANT(fmt, ...) \
+ ::tracing_perfetto::traceFormatInstant(ATRACE_TAG, fmt, ##__VA_ARGS__)
+#define SFTRACE_INSTANT_FOR_TRACK(trackName, name) \
+ ::tracing_perfetto::traceInstantForTrack(ATRACE_TAG, trackName, name)
+#define SFTRACE_INT(name, value) ::tracing_perfetto::traceCounter32(ATRACE_TAG, name, value)
+#define SFTRACE_INT64(name, value) ::tracing_perfetto::traceCounter(ATRACE_TAG, name, value)
+
+// SFTRACE_NAME traces from its location until the end of its enclosing scope.
+#define _PASTE(x, y) x##y
+#define PASTE(x, y) _PASTE(x, y)
+#define SFTRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(name)
+// SFTRACE_CALL is an SFTRACE_NAME that uses the current function name.
+#define SFTRACE_CALL() SFTRACE_NAME(__FUNCTION__)
+
+#define SFTRACE_FORMAT(fmt, ...) \
+ ::android::ScopedTrace PASTE(___tracer, __LINE__)(fmt, ##__VA_ARGS__)
+
+#define ALOGE_AND_TRACE(fmt, ...) \
+ do { \
+ ALOGE(fmt, ##__VA_ARGS__); \
+ SFTRACE_FORMAT_INSTANT(fmt, ##__VA_ARGS__); \
+ } while (false)
+
+namespace android {
+
+class ScopedTrace {
+public:
+ template <typename... Args>
+ inline ScopedTrace(const char* fmt, Args&&... args) {
+ ::tracing_perfetto::traceFormatBegin(ATRACE_TAG, fmt, std::forward<Args>(args)...);
+ }
+ inline ScopedTrace(const char* name) { SFTRACE_BEGIN(name); }
+ inline ~ScopedTrace() { SFTRACE_END(); }
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index f77b137..0a69a72 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -8,14 +8,9 @@
default_team: "trendy_team_android_core_graphics_stack",
}
-cc_library {
- name: "liblayers_proto",
+cc_defaults {
+ name: "libsurfaceflinger_proto_deps",
export_include_dirs: ["include"],
-
- srcs: [
- "LayerProtoParser.cpp",
- ],
-
static_libs: [
"libperfetto_client_experimental",
],
@@ -31,24 +26,15 @@
],
shared_libs: [
- "android.hardware.graphics.common@1.1",
- "libgui",
- "libui",
"libprotobuf-cpp-lite",
- "libbase",
],
- cppflags: [
- "-Werror",
- "-Wno-unused-parameter",
- "-Wno-format",
- "-Wno-c++98-compat-pedantic",
- "-Wno-float-conversion",
- "-Wno-disabled-macro-expansion",
- "-Wno-float-equal",
- "-Wno-sign-conversion",
- "-Wno-padded",
- "-Wno-old-style-cast",
- "-Wno-undef",
+ header_libs: [
+ "libsurfaceflinger_proto_headers",
],
}
+
+cc_library_headers {
+ name: "libsurfaceflinger_proto_headers",
+ export_include_dirs: ["include"],
+}
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
deleted file mode 100644
index c3d0a40..0000000
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <android-base/stringprintf.h>
-#include <layerproto/LayerProtoParser.h>
-#include <ui/DebugUtils.h>
-
-using android::base::StringAppendF;
-using android::base::StringPrintf;
-
-namespace android {
-namespace surfaceflinger {
-
-bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) {
- uint32_t ls = lhs->layerStack;
- uint32_t rs = rhs->layerStack;
- if (ls != rs) return ls < rs;
-
- int32_t lz = lhs->z;
- int32_t rz = rhs->z;
- if (lz != rz) {
- return lz < rz;
- }
-
- return lhs->id < rhs->id;
-}
-
-LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(
- const perfetto::protos::LayersProto& layersProto) {
- LayerTree layerTree;
- layerTree.allLayers = generateLayerList(layersProto);
-
- // find and sort the top-level layers
- for (Layer& layer : layerTree.allLayers) {
- if (layer.parent == nullptr) {
- layerTree.topLevelLayers.push_back(&layer);
- }
- }
- std::sort(layerTree.topLevelLayers.begin(), layerTree.topLevelLayers.end(), sortLayers);
-
- return layerTree;
-}
-
-std::vector<LayerProtoParser::Layer> LayerProtoParser::generateLayerList(
- const perfetto::protos::LayersProto& layersProto) {
- std::vector<Layer> layerList;
- std::unordered_map<int32_t, Layer*> layerMap;
-
- // build the layer list and the layer map
- layerList.reserve(layersProto.layers_size());
- layerMap.reserve(layersProto.layers_size());
- for (int i = 0; i < layersProto.layers_size(); i++) {
- layerList.emplace_back(generateLayer(layersProto.layers(i)));
- // this works because layerList never changes capacity
- layerMap[layerList.back().id] = &layerList.back();
- }
-
- // fix up children and relatives
- for (int i = 0; i < layersProto.layers_size(); i++) {
- updateChildrenAndRelative(layersProto.layers(i), layerMap);
- }
-
- return layerList;
-}
-
-LayerProtoParser::Layer LayerProtoParser::generateLayer(
- const perfetto::protos::LayerProto& layerProto) {
- Layer layer;
- layer.id = layerProto.id();
- layer.name = layerProto.name();
- layer.type = layerProto.type();
- layer.transparentRegion = generateRegion(layerProto.transparent_region());
- layer.visibleRegion = generateRegion(layerProto.visible_region());
- layer.damageRegion = generateRegion(layerProto.damage_region());
- layer.layerStack = layerProto.layer_stack();
- layer.z = layerProto.z();
- layer.position = {layerProto.position().x(), layerProto.position().y()};
- layer.requestedPosition = {layerProto.requested_position().x(),
- layerProto.requested_position().y()};
- layer.size = {layerProto.size().w(), layerProto.size().h()};
- layer.crop = generateRect(layerProto.crop());
- layer.isOpaque = layerProto.is_opaque();
- layer.invalidate = layerProto.invalidate();
- layer.dataspace = layerProto.dataspace();
- layer.pixelFormat = layerProto.pixel_format();
- layer.color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
- layerProto.color().a()};
- layer.requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
- layerProto.requested_color().b(), layerProto.requested_color().a()};
- layer.flags = layerProto.flags();
- layer.transform = generateTransform(layerProto.transform());
- layer.requestedTransform = generateTransform(layerProto.requested_transform());
- layer.activeBuffer = generateActiveBuffer(layerProto.active_buffer());
- layer.bufferTransform = generateTransform(layerProto.buffer_transform());
- layer.queuedFrames = layerProto.queued_frames();
- layer.refreshPending = layerProto.refresh_pending();
- layer.isProtected = layerProto.is_protected();
- layer.isTrustedOverlay = layerProto.is_trusted_overlay();
- layer.cornerRadius = layerProto.corner_radius();
- layer.backgroundBlurRadius = layerProto.background_blur_radius();
- for (const auto& entry : layerProto.metadata()) {
- const std::string& dataStr = entry.second;
- std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
- outData.resize(dataStr.size());
- memcpy(outData.data(), dataStr.data(), dataStr.size());
- }
- layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
- layer.shadowRadius = layerProto.shadow_radius();
- layer.ownerUid = layerProto.owner_uid();
- return layer;
-}
-
-LayerProtoParser::Region LayerProtoParser::generateRegion(
- const perfetto::protos::RegionProto& regionProto) {
- LayerProtoParser::Region region;
- for (int i = 0; i < regionProto.rect_size(); i++) {
- const perfetto::protos::RectProto& rectProto = regionProto.rect(i);
- region.rects.push_back(generateRect(rectProto));
- }
-
- return region;
-}
-
-LayerProtoParser::Rect LayerProtoParser::generateRect(
- const perfetto::protos::RectProto& rectProto) {
- LayerProtoParser::Rect rect;
- rect.left = rectProto.left();
- rect.top = rectProto.top();
- rect.right = rectProto.right();
- rect.bottom = rectProto.bottom();
-
- return rect;
-}
-
-LayerProtoParser::FloatRect LayerProtoParser::generateFloatRect(
- const perfetto::protos::FloatRectProto& rectProto) {
- LayerProtoParser::FloatRect rect;
- rect.left = rectProto.left();
- rect.top = rectProto.top();
- rect.right = rectProto.right();
- rect.bottom = rectProto.bottom();
-
- return rect;
-}
-
-LayerProtoParser::Transform LayerProtoParser::generateTransform(
- const perfetto::protos::TransformProto& transformProto) {
- LayerProtoParser::Transform transform;
- transform.dsdx = transformProto.dsdx();
- transform.dtdx = transformProto.dtdx();
- transform.dsdy = transformProto.dsdy();
- transform.dtdy = transformProto.dtdy();
-
- return transform;
-}
-
-LayerProtoParser::ActiveBuffer LayerProtoParser::generateActiveBuffer(
- const perfetto::protos::ActiveBufferProto& activeBufferProto) {
- LayerProtoParser::ActiveBuffer activeBuffer;
- activeBuffer.width = activeBufferProto.width();
- activeBuffer.height = activeBufferProto.height();
- activeBuffer.stride = activeBufferProto.stride();
- activeBuffer.format = activeBufferProto.format();
-
- return activeBuffer;
-}
-
-void LayerProtoParser::updateChildrenAndRelative(const perfetto::protos::LayerProto& layerProto,
- std::unordered_map<int32_t, Layer*>& layerMap) {
- auto currLayer = layerMap[layerProto.id()];
-
- for (int i = 0; i < layerProto.children_size(); i++) {
- if (layerMap.count(layerProto.children(i)) > 0) {
- currLayer->children.push_back(layerMap[layerProto.children(i)]);
- }
- }
-
- for (int i = 0; i < layerProto.relatives_size(); i++) {
- if (layerMap.count(layerProto.relatives(i)) > 0) {
- currLayer->relatives.push_back(layerMap[layerProto.relatives(i)]);
- }
- }
-
- if (layerProto.has_parent()) {
- if (layerMap.count(layerProto.parent()) > 0) {
- currLayer->parent = layerMap[layerProto.parent()];
- }
- }
-
- if (layerProto.has_z_order_relative_of()) {
- if (layerMap.count(layerProto.z_order_relative_of()) > 0) {
- currLayer->zOrderRelativeOf = layerMap[layerProto.z_order_relative_of()];
- }
- }
-}
-
-std::string LayerProtoParser::layerTreeToString(const LayerTree& layerTree) {
- std::string result;
- for (const LayerProtoParser::Layer* layer : layerTree.topLevelLayers) {
- if (layer->zOrderRelativeOf != nullptr) {
- continue;
- }
- result.append(layerToString(layer));
- }
-
- return result;
-}
-
-std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
- std::string result;
-
- std::vector<Layer*> traverse(layer->relatives);
- for (LayerProtoParser::Layer* child : layer->children) {
- if (child->zOrderRelativeOf != nullptr) {
- continue;
- }
-
- traverse.push_back(child);
- }
-
- std::sort(traverse.begin(), traverse.end(), sortLayers);
-
- size_t i = 0;
- for (; i < traverse.size(); i++) {
- auto& relative = traverse[i];
- if (relative->z >= 0) {
- break;
- }
- result.append(layerToString(relative));
- }
- result.append(layer->to_string());
- result.append("\n");
- for (; i < traverse.size(); i++) {
- auto& relative = traverse[i];
- result.append(layerToString(relative));
- }
-
- return result;
-}
-
-std::string LayerProtoParser::ActiveBuffer::to_string() const {
- return StringPrintf("[%4ux%4u:%4u,%s]", width, height, stride,
- decodePixelFormat(format).c_str());
-}
-
-std::string LayerProtoParser::Transform::to_string() const {
- return StringPrintf("[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(dsdx),
- static_cast<double>(dtdx), static_cast<double>(dsdy),
- static_cast<double>(dtdy));
-}
-
-std::string LayerProtoParser::Rect::to_string() const {
- return StringPrintf("[%3d, %3d, %3d, %3d]", left, top, right, bottom);
-}
-
-std::string LayerProtoParser::FloatRect::to_string() const {
- return StringPrintf("[%.2f, %.2f, %.2f, %.2f]", left, top, right, bottom);
-}
-
-std::string LayerProtoParser::Region::to_string(const char* what) const {
- std::string result =
- StringPrintf(" Region %s (this=%lx count=%d)\n", what, static_cast<unsigned long>(id),
- static_cast<int>(rects.size()));
-
- for (auto& rect : rects) {
- StringAppendF(&result, " %s\n", rect.to_string().c_str());
- }
-
- return result;
-}
-
-std::string LayerProtoParser::Layer::to_string() const {
- std::string result;
- StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid);
- result.append(transparentRegion.to_string("TransparentRegion").c_str());
- result.append(visibleRegion.to_string("VisibleRegion").c_str());
- result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
-
- StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", layerStack,
- z, static_cast<double>(position.x), static_cast<double>(position.y), size.x,
- size.y);
-
- StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
- StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
- StringAppendF(&result, "isProtected=%1d, ", isProtected);
- StringAppendF(&result, "isTrustedOverlay=%1d, ", isTrustedOverlay);
- StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
- StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
- StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
- StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius);
- StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
- static_cast<double>(color.r), static_cast<double>(color.g),
- static_cast<double>(color.b), static_cast<double>(color.a), flags);
- StringAppendF(&result, "tr=%s", transform.to_string().c_str());
- result.append("\n");
- StringAppendF(&result, " parent=%s\n", parent == nullptr ? "none" : parent->name.c_str());
- StringAppendF(&result, " zOrderRelativeOf=%s\n",
- zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
- StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str());
- StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str());
- StringAppendF(&result, " queued-frames=%d", queuedFrames);
- StringAppendF(&result, " metadata={");
- bool first = true;
- for (const auto& entry : metadata.mMap) {
- if (!first) result.append(", ");
- first = false;
- result.append(metadata.itemToString(entry.first, ":"));
- }
- result.append("},");
- StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str());
- StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius);
- return result;
-}
-
-} // namespace surfaceflinger
-} // namespace android
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
deleted file mode 100644
index 79c3982..0000000
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 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 <layerproto/LayerProtoHeader.h>
-
-#include <gui/LayerMetadata.h>
-#include <math/vec4.h>
-
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-using android::gui::LayerMetadata;
-
-namespace android {
-namespace surfaceflinger {
-
-class LayerProtoParser {
-public:
- class ActiveBuffer {
- public:
- uint32_t width;
- uint32_t height;
- uint32_t stride;
- int32_t format;
-
- std::string to_string() const;
- };
-
- class Transform {
- public:
- float dsdx;
- float dtdx;
- float dsdy;
- float dtdy;
-
- std::string to_string() const;
- };
-
- class Rect {
- public:
- int32_t left;
- int32_t top;
- int32_t right;
- int32_t bottom;
-
- std::string to_string() const;
- };
-
- class FloatRect {
- public:
- float left;
- float top;
- float right;
- float bottom;
-
- std::string to_string() const;
- };
-
- class Region {
- public:
- uint64_t id;
- std::vector<Rect> rects;
-
- std::string to_string(const char* what) const;
- };
-
- class Layer {
- public:
- int32_t id;
- std::string name;
- std::vector<Layer*> children;
- std::vector<Layer*> relatives;
- std::string type;
- LayerProtoParser::Region transparentRegion;
- LayerProtoParser::Region visibleRegion;
- LayerProtoParser::Region damageRegion;
- uint32_t layerStack;
- int32_t z;
- float2 position;
- float2 requestedPosition;
- int2 size;
- LayerProtoParser::Rect crop;
- bool isOpaque;
- bool invalidate;
- std::string dataspace;
- std::string pixelFormat;
- half4 color;
- half4 requestedColor;
- uint32_t flags;
- Transform transform;
- Transform requestedTransform;
- Layer* parent = 0;
- Layer* zOrderRelativeOf = 0;
- LayerProtoParser::ActiveBuffer activeBuffer;
- Transform bufferTransform;
- int32_t queuedFrames;
- bool refreshPending;
- bool isProtected;
- bool isTrustedOverlay;
- float cornerRadius;
- int backgroundBlurRadius;
- LayerMetadata metadata;
- LayerProtoParser::FloatRect cornerRadiusCrop;
- float shadowRadius;
- uid_t ownerUid;
-
- std::string to_string() const;
- };
-
- class LayerTree {
- public:
- // all layers in LayersProto and in the original order
- std::vector<Layer> allLayers;
-
- // pointers to top-level layers in allLayers
- std::vector<Layer*> topLevelLayers;
- };
-
- static LayerTree generateLayerTree(const perfetto::protos::LayersProto& layersProto);
- static std::string layerTreeToString(const LayerTree& layerTree);
-
-private:
- static std::vector<Layer> generateLayerList(const perfetto::protos::LayersProto& layersProto);
- static LayerProtoParser::Layer generateLayer(const perfetto::protos::LayerProto& layerProto);
- static LayerProtoParser::Region generateRegion(
- const perfetto::protos::RegionProto& regionProto);
- static LayerProtoParser::Rect generateRect(const perfetto::protos::RectProto& rectProto);
- static LayerProtoParser::FloatRect generateFloatRect(
- const perfetto::protos::FloatRectProto& rectProto);
- static LayerProtoParser::Transform generateTransform(
- const perfetto::protos::TransformProto& transformProto);
- static LayerProtoParser::ActiveBuffer generateActiveBuffer(
- const perfetto::protos::ActiveBufferProto& activeBufferProto);
- static void updateChildrenAndRelative(const perfetto::protos::LayerProto& layerProto,
- std::unordered_map<int32_t, Layer*>& layerMap);
-
- static std::string layerToString(const LayerProtoParser::Layer* layer);
-};
-
-} // namespace surfaceflinger
-} // namespace android
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index f4d4ee9..102e2b6 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -4,10 +4,10 @@
container: "system"
flag {
- name: "adpf_gpu_sf"
- namespace: "game"
- description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
- bug: "284324521"
+ name: "adpf_gpu_sf"
+ namespace: "game"
+ description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
+ bug: "284324521"
} # adpf_gpu_sf
flag {
@@ -21,18 +21,29 @@
}
} # ce_fence_promise
- flag {
- name: "commit_not_composited"
- namespace: "core_graphics"
- description: "mark frames as non janky if the transaction resulted in no composition"
- bug: "340633280"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- } # commit_not_composited
+flag {
+ name: "commit_not_composited"
+ namespace: "core_graphics"
+ description: "mark frames as non janky if the transaction resulted in no composition"
+ bug: "340633280"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # commit_not_composited
- flag {
+flag {
+ name: "correct_dpi_with_display_size"
+ namespace: "core_graphics"
+ description: "indicate whether missing or likely incorrect dpi should be corrected using the display size."
+ bug: "328425848"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # correct_dpi_with_display_size
+
+flag {
name: "deprecate_vsync_sf"
namespace: "core_graphics"
description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
@@ -43,7 +54,7 @@
}
} # deprecate_vsync_sf
- flag {
+flag {
name: "detached_mirror"
namespace: "window_surfaces"
description: "Ignore local transform when mirroring a partial hierarchy"
@@ -55,6 +66,17 @@
} # detached_mirror
flag {
+ name: "filter_frames_before_trace_starts"
+ namespace: "core_graphics"
+ description: "Do not trace FrameTimeline events for frames started before the trace started"
+ bug: "364194637"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # filter_frames_before_trace_starts
+
+flag {
name: "flush_buffer_slots_to_uncache"
namespace: "core_graphics"
description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers."
@@ -96,11 +118,11 @@
} # latch_unsignaled_with_auto_refresh_changed
flag {
- name: "local_tonemap_screenshots"
- namespace: "core_graphics"
- description: "Enables local tonemapping when capturing screenshots"
- bug: "329464641"
- is_fixed_read_only: true
+ name: "local_tonemap_screenshots"
+ namespace: "core_graphics"
+ description: "Enables local tonemapping when capturing screenshots"
+ bug: "329464641"
+ is_fixed_read_only: true
} # local_tonemap_screenshots
flag {
@@ -114,6 +136,14 @@
}
} # single_hop_screenshot
+flag {
+ name: "true_hdr_screenshots"
+ namespace: "core_graphics"
+ description: "Enables screenshotting display content in HDR, sans tone mapping"
+ bug: "329470026"
+ is_fixed_read_only: true
+} # true_hdr_screenshots
+
flag {
name: "override_trusted_overlay"
namespace: "window_surfaces"
@@ -126,6 +156,14 @@
} # override_trusted_overlay
flag {
+ name: "view_set_requested_frame_rate_mrr"
+ namespace: "core_graphics"
+ description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices"
+ bug: "352206100"
+ is_fixed_read_only: true
+} # view_set_requested_frame_rate_mrr
+
+flag {
name: "vrr_bugfix_24q4"
namespace: "core_graphics"
description: "bug fixes for VRR"
@@ -136,4 +174,15 @@
}
} # vrr_bugfix_24q4
+flag {
+ name: "vrr_bugfix_dropped_frame"
+ namespace: "core_graphics"
+ description: "bug fix for VRR dropped frame"
+ bug: "343603085"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # vrr_bugfix_dropped_frame
+
# IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 38fc977..4d5c0fd 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -28,6 +28,7 @@
"android.hardware.graphics.common-ndk_shared",
"surfaceflinger_defaults",
"libsurfaceflinger_common_test_deps",
+ "libsurfaceflinger_proto_deps",
],
test_suites: ["device-tests"],
srcs: [
@@ -58,14 +59,12 @@
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
"SetGeometry_test.cpp",
- "Stress_test.cpp",
"TextureFiltering_test.cpp",
"VirtualDisplay_test.cpp",
"WindowInfosListener_test.cpp",
],
data: ["SurfaceFlinger_test.filter"],
static_libs: [
- "liblayers_proto",
"android.hardware.graphics.composer@2.1",
"libsurfaceflinger_common",
],
@@ -121,7 +120,6 @@
"libEGL",
"libGLESv2",
"libgui",
- "liblayers_proto",
"liblog",
"libprotobuf-cpp-full",
"libui",
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index 4f41a81..222642f 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -18,7 +18,7 @@
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index d74bd55..efab7b8 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -42,8 +42,13 @@
* through saved callback. */
class BufferListener : public ConsumerBase::FrameAvailableListener {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferListener(sp<BufferItemConsumer> consumer, BufferCallback callback)
+#else
BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
- : mConsumer(consumer), mCallback(callback) {}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ : mConsumer(consumer), mCallback(callback) {
+ }
void onFrameAvailable(const BufferItem& /*item*/) {
BufferItem item;
@@ -55,7 +60,11 @@
}
private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> mConsumer;
+#else
sp<IGraphicBufferConsumer> mConsumer;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
BufferCallback mCallback;
};
@@ -63,6 +72,16 @@
* queue. */
void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
BufferCallback callback) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer = sp<BufferItemConsumer>::make(GraphicBuffer::USAGE_HW_TEXTURE);
+ mBufferItemConsumer->setDefaultBufferSize(width, height);
+ mBufferItemConsumer->setDefaultBufferFormat(format);
+
+ mListener = sp<BufferListener>::make(mBufferItemConsumer, callback);
+ mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+ mSurface = mBufferItemConsumer->getSurface();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -77,6 +96,7 @@
mBufferItemConsumer->setFrameAvailableListener(mListener);
mSurface = sp<Surface>::make(producer, true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
/* Used by Egl manager. The surface is never displayed. */
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index ebe11fb..e6fed63 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -20,12 +20,13 @@
#include <android/gui/ISurfaceComposer.h>
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <utils/String8.h>
#include <functional>
@@ -276,10 +277,10 @@
TEST_F(CredentialsTest, CaptureLayersTest) {
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
- std::function<status_t()> condition = [=]() {
+ std::function<status_t()> condition = [=, this]() {
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mBGSurfaceControl->getHandle();
- captureArgs.sourceCrop = {0, 0, 1, 1};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(0, 0, 1, 1);
ScreenCaptureResults captureResults;
return ScreenCapture::captureLayers(captureArgs, captureResults);
@@ -396,6 +397,56 @@
}
}
+TEST_F(CredentialsTest, DisplayTransactionPermissionTest) {
+ const auto display = getFirstDisplayToken();
+
+ ui::DisplayState displayState;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ const ui::Rotation initialOrientation = displayState.orientation;
+
+ // Set display orientation from an untrusted process. This should fail silently.
+ {
+ UIDFaker f{AID_BIN};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did not change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+
+ // Set display orientation from a trusted process.
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation + ui::ROTATION_90, displayState.orientation);
+
+ // Reset orientation
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation, layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 15a98df..cc57e11 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -28,66 +28,6 @@
namespace test {
-TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
- DisplayCaptureArgs args;
- args.pixelFormat = ui::PixelFormat::RGB_565;
- args.sourceCrop = Rect(0, 0, 500, 200);
- args.frameScaleX = 2;
- args.frameScaleY = 4;
- args.captureSecureLayers = true;
- args.displayToken = sp<BBinder>::make();
- args.width = 10;
- args.height = 20;
- args.grayscale = true;
-
- Parcel p;
- args.writeToParcel(&p);
- p.setDataPosition(0);
-
- DisplayCaptureArgs args2;
- args2.readFromParcel(&p);
-
- ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
- ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
- ASSERT_EQ(args.frameScaleX, args2.frameScaleX);
- ASSERT_EQ(args.frameScaleY, args2.frameScaleY);
- ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
- ASSERT_EQ(args.displayToken, args2.displayToken);
- ASSERT_EQ(args.width, args2.width);
- ASSERT_EQ(args.height, args2.height);
- ASSERT_EQ(args.grayscale, args2.grayscale);
-}
-
-TEST(LayerStateTest, ParcellingLayerCaptureArgs) {
- LayerCaptureArgs args;
- args.pixelFormat = ui::PixelFormat::RGB_565;
- args.sourceCrop = Rect(0, 0, 500, 200);
- args.frameScaleX = 2;
- args.frameScaleY = 4;
- args.captureSecureLayers = true;
- args.layerHandle = sp<BBinder>::make();
- args.excludeHandles = {sp<BBinder>::make(), sp<BBinder>::make()};
- args.childrenOnly = false;
- args.grayscale = true;
-
- Parcel p;
- args.writeToParcel(&p);
- p.setDataPosition(0);
-
- LayerCaptureArgs args2;
- args2.readFromParcel(&p);
-
- ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
- ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
- ASSERT_EQ(args.frameScaleX, args2.frameScaleX);
- ASSERT_EQ(args.frameScaleY, args2.frameScaleY);
- ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
- ASSERT_EQ(args.layerHandle, args2.layerHandle);
- ASSERT_EQ(args.excludeHandles, args2.excludeHandles);
- ASSERT_EQ(args.childrenOnly, args2.childrenOnly);
- ASSERT_EQ(args.grayscale, args2.grayscale);
-}
-
TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFence) {
ScreenCaptureResults results;
results.buffer = sp<GraphicBuffer>::make(100u, 200u, PIXEL_FORMAT_RGBA_8888, 1u, 0u);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 5b056d0..03f9005 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -23,7 +23,7 @@
#include <cutils/properties.h>
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index f9b4bba..76bae41 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -18,6 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <gui/AidlUtil.h>
#include <gui/BufferItemConsumer.h>
#include <private/android_filesystem_config.h>
#include "TransactionTestHarnesses.h"
@@ -64,7 +65,7 @@
// only layerB is in this range
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = parent->getHandle();
- captureArgs.sourceCrop = {0, 0, 32, 32};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(32, 32);
ScreenCapture::captureLayers(&screenshot, captureArgs);
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index d97d433..6cc1c51 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -20,6 +20,7 @@
#include <android-base/properties.h>
#include <common/FlagManager.h>
+#include <gui/AidlUtil.h>
#include <private/android_filesystem_config.h>
#include "LayerTransactionTest.h"
#include "utils/TransactionUtils.h"
@@ -350,7 +351,7 @@
// Capture just the mirror layer and child.
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mirrorParent->getHandle();
- captureArgs.sourceCrop = childBounds;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(childBounds);
std::unique_ptr<ScreenCapture> shot;
ScreenCapture::captureLayers(&shot, captureArgs);
shot->expectSize(childBounds.width(), childBounds.height());
diff --git a/services/surfaceflinger/tests/OWNERS b/services/surfaceflinger/tests/OWNERS
index 56f2f1b..7857961 100644
--- a/services/surfaceflinger/tests/OWNERS
+++ b/services/surfaceflinger/tests/OWNERS
@@ -4,5 +4,5 @@
per-file Layer* = set noparent
per-file Layer* = pdwilliams@google.com, vishnun@google.com, melodymhsu@google.com
-per-file LayerHistoryTest.cpp = file:/services/surfaceflinger/OWNERS
+per-file LayerHistoryIntegrationTest.cpp = file:/services/surfaceflinger/OWNERS
per-file LayerInfoTest.cpp = file:/services/surfaceflinger/OWNERS
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 9a78550..c62f493 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -20,6 +20,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <gui/AidlUtil.h>
#include <private/android_filesystem_config.h>
#include <ui/DisplayState.h>
@@ -65,7 +66,7 @@
.show(mFGSurfaceControl);
});
- mCaptureArgs.sourceCrop = mDisplayRect;
+ mCaptureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(mDisplayRect);
mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
@@ -112,7 +113,7 @@
shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
- mCaptureArgs.captureSecureLayers = true;
+ mCaptureArgs.captureArgs.captureSecureLayers = true;
// AID_SYSTEM is allowed to capture secure content.
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
@@ -164,7 +165,7 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- mCaptureArgs.captureSecureLayers = true;
+ mCaptureArgs.captureArgs.captureSecureLayers = true;
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
@@ -198,8 +199,8 @@
.apply();
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = childLayer->getHandle();
- captureArgs.sourceCrop = size;
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(size);
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent hidden");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -208,7 +209,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent not visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -218,7 +219,7 @@
}
Transaction().show(parentLayer).apply();
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -227,7 +228,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -259,8 +260,8 @@
.apply();
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = childLayer->getHandle();
- captureArgs.sourceCrop = size;
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(size);
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent hidden");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -269,7 +270,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent not visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -279,7 +280,7 @@
}
Transaction().show(parentLayer).apply();
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -288,7 +289,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -361,14 +362,14 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = fgHandle;
captureArgs.childrenOnly = true;
- captureArgs.excludeHandles = {child2->getHandle()};
+ captureArgs.captureArgs.excludeHandles = {child2->getHandle()};
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->checkPixel(0, 0, 200, 200, 200);
}
TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
- mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
+ mCaptureArgs.captureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectBGColor(0, 0);
// Doesn't capture FG layer which is at 64, 64
@@ -401,7 +402,7 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = fgHandle;
captureArgs.childrenOnly = true;
- captureArgs.excludeHandles = {child2->getHandle()};
+ captureArgs.captureArgs.excludeHandles = {child2->getHandle()};
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->checkPixel(0, 0, 200, 200, 200);
@@ -418,7 +419,7 @@
// Captures child
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = child->getHandle();
- captureArgs.sourceCrop = {0, 0, 10, 20};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(10, 20);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
// Area outside of child's bounds is transparent.
@@ -481,7 +482,7 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = child->getHandle();
- captureArgs.sourceCrop = {0, 0, 10, 10};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(10, 10);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
@@ -623,7 +624,7 @@
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- captureArgs.sourceCrop = {0, 0, 30, 30};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(30, 30);
ScreenCapture::captureLayers(&mCapture, captureArgs);
// Capturing the cropped screen, cropping out the shown red area, should leave only the blue
// area visible.
@@ -658,8 +659,8 @@
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- captureArgs.frameScaleX = 0.5f;
- captureArgs.frameScaleY = 0.5f;
+ captureArgs.captureArgs.frameScaleX = 0.5f;
+ captureArgs.captureArgs.frameScaleY = 0.5f;
sleep(1);
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -689,8 +690,8 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = redLayer->getHandle();
- captureArgs.frameScaleX = INT32_MAX / 60;
- captureArgs.frameScaleY = INT32_MAX / 60;
+ captureArgs.captureArgs.frameScaleX = INT32_MAX / 60;
+ captureArgs.captureArgs.frameScaleY = INT32_MAX / 60;
ScreenCaptureResults captureResults;
ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(captureArgs, captureResults));
@@ -736,7 +737,7 @@
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
// Passing flag secure so the blue layer should be screenshot too.
- args.captureSecureLayers = true;
+ args.captureArgs.captureSecureLayers = true;
ScreenCapture::captureLayers(&mCapture, args);
mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE);
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
@@ -780,7 +781,7 @@
// Reading color data will expectedly result in crash, only check usage bit
// b/309965549 Checking that the usage bit is protected does not work for
// devices that do not support usage protected.
- mCaptureArgs.allowProtected = true;
+ mCaptureArgs.captureArgs.allowProtected = true;
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
// ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED &
// captureResults.buffer->getUsage());
@@ -898,7 +899,7 @@
// Make screenshot request with current uid set. No layers were created with the current
// uid so screenshot will be black.
- captureArgs.uid = fakeUid;
+ captureArgs.captureArgs.uid = fakeUid;
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT);
mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT);
@@ -935,7 +936,7 @@
mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
// Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot.
- captureArgs.uid = -1;
+ captureArgs.captureArgs.uid = -1;
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED);
mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255});
@@ -955,7 +956,7 @@
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
- captureArgs.grayscale = true;
+ captureArgs.captureArgs.grayscale = true;
const uint8_t tolerance = 1;
@@ -1052,7 +1053,7 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mirroredLayer->getHandle();
- captureArgs.sourceCrop = Rect(0, 0, 1, 1);
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(1, 1);
// Screenshot path should only use the children of the layer hierarchy so
// that it will not create a new snapshot. A snapshot would otherwise be
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
deleted file mode 100644
index b30df5e..0000000
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <gtest/gtest.h>
-
-#include <gui/SurfaceComposerClient.h>
-
-#include <utils/String8.h>
-
-#include <thread>
-#include <functional>
-#include <layerproto/LayerProtoParser.h>
-
-namespace android {
-
-TEST(SurfaceFlingerStress, create_and_destroy) {
- auto do_stress = []() {
- sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
- ASSERT_EQ(NO_ERROR, client->initCheck());
- for (int j = 0; j < 1000; j++) {
- auto surf = client->createSurface(String8("t"), 100, 100,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(surf != nullptr);
- surf.clear();
- }
- };
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(std::thread(do_stress));
- }
- for (auto& thread : threads) {
- thread.join();
- }
-}
-
-perfetto::protos::LayersProto generateLayerProto() {
- perfetto::protos::LayersProto layersProto;
- std::array<perfetto::protos::LayerProto*, 10> layers = {};
- for (size_t i = 0; i < layers.size(); ++i) {
- layers[i] = layersProto.add_layers();
- layers[i]->set_id(i);
- }
-
- layers[0]->add_children(1);
- layers[1]->set_parent(0);
- layers[0]->add_children(2);
- layers[2]->set_parent(0);
- layers[0]->add_children(3);
- layers[3]->set_parent(0);
- layers[2]->add_children(4);
- layers[4]->set_parent(2);
- layers[3]->add_children(5);
- layers[5]->set_parent(3);
- layers[5]->add_children(6);
- layers[6]->set_parent(5);
- layers[5]->add_children(7);
- layers[7]->set_parent(5);
- layers[6]->add_children(8);
- layers[8]->set_parent(6);
-
- layers[4]->set_z_order_relative_of(3);
- layers[3]->add_relatives(4);
- layers[8]->set_z_order_relative_of(9);
- layers[9]->add_relatives(8);
- layers[3]->set_z_order_relative_of(1);
- layers[1]->add_relatives(3);
-
-/* ----------------------------
- * - 0 - - 9 -
- * / | \
- * 1 2 3(1)
- * | |
- * 4(3) 5
- * / \
- * 6 7
- * |
- * 8(9)
- * -------------------------- */
-
- return layersProto;
-}
-
-TEST(LayerProtoStress, mem_info) {
- std::string cmd = "dumpsys meminfo ";
- cmd += std::to_string(getpid());
- system(cmd.c_str());
- for (int i = 0; i < 100000; i++) {
- perfetto::protos::LayersProto layersProto = generateLayerProto();
- auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto);
- surfaceflinger::LayerProtoParser::layerTreeToString(layerTree);
- }
- system(cmd.c_str());
-}
-
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index c5d118c..3f39cf6 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <gtest/gtest.h>
-#include <gui/DisplayCaptureArgs.h>
+#include <gui/AidlUtil.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -84,7 +85,7 @@
};
TEST_F(TextureFilteringTest, NoFiltering) {
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -93,7 +94,7 @@
}
TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -105,7 +106,7 @@
TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -114,9 +115,9 @@
// Expect filtering because the output source crop is stretched to the output buffer's size.
TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
- captureArgs.frameScaleX = 2;
- captureArgs.frameScaleY = 2;
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.frameScaleX = 2;
+ captureArgs.captureArgs.frameScaleY = 2;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -127,9 +128,9 @@
// buffer's size.
TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- captureArgs.frameScaleX = 2;
- captureArgs.frameScaleY = 2;
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.frameScaleX = 2;
+ captureArgs.captureArgs.frameScaleY = 2;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -139,8 +140,8 @@
// Expect filtering because the layer is scaled up.
TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.frameScaleX = 2;
- captureArgs.frameScaleY = 2;
+ captureArgs.captureArgs.frameScaleX = 2;
+ captureArgs.captureArgs.frameScaleY = 2;
ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 100, 200}, {100, 0, 200, 200});
@@ -149,7 +150,7 @@
// Expect no filtering because the output buffer's size matches the source crop.
TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
@@ -162,7 +163,7 @@
Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
@@ -172,7 +173,7 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
@@ -206,7 +207,7 @@
Transaction().setPosition(mParent, 100, 100).apply();
captureArgs.layerHandle = mParent->getHandle();
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index af3cb9a..67a5247 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -16,6 +16,7 @@
#ifndef ANDROID_TRANSACTION_TEST_HARNESSES
#define ANDROID_TRANSACTION_TEST_HARNESSES
+#include <com_android_graphics_libgui_flags.h>
#include <common/FlagManager.h>
#include <ui/DisplayState.h>
@@ -51,6 +52,16 @@
const ui::Size& resolution = displayMode.resolution;
sp<IBinder> vDisplay;
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> itemConsumer = sp<BufferItemConsumer>::make(
+ // Sample usage bits from screenrecord
+ GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_SW_READ_OFTEN);
+ sp<BufferListener> listener = sp<BufferListener>::make(this);
+ itemConsumer->setFrameAvailableListener(listener);
+ itemConsumer->setName(String8("Virtual disp consumer"));
+ itemConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
sp<BufferItemConsumer> itemConsumer;
@@ -65,6 +76,7 @@
GRALLOC_USAGE_SW_READ_OFTEN);
sp<BufferListener> listener = sp<BufferListener>::make(this);
itemConsumer->setFrameAvailableListener(listener);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
static const std::string kDisplayName("VirtualDisplay");
vDisplay = SurfaceComposerClient::createVirtualDisplay(kDisplayName,
@@ -76,7 +88,12 @@
SurfaceComposerClient::getDefault()->mirrorDisplay(displayId);
SurfaceComposerClient::Transaction t;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ t.setDisplaySurface(vDisplay,
+ itemConsumer->getSurface()->getIGraphicBufferProducer());
+#else
t.setDisplaySurface(vDisplay, producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
t.setDisplayProjection(vDisplay, displayState.orientation,
Rect(displayState.layerStackSpaceRect), Rect(resolution));
if (FlagManager::getInstance().ce_fence_promise()) {
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index cd66dd2..d69378c 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -27,6 +27,12 @@
class VirtualDisplayTest : public ::testing::Test {
protected:
void SetUp() override {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mGLConsumer = sp<GLConsumer>::make(GLConsumer::TEXTURE_EXTERNAL, true, false, false);
+ mGLConsumer->setName(String8("Virtual disp consumer"));
+ mGLConsumer->setDefaultBufferSize(100, 100);
+ mProducer = mGLConsumer->getSurface()->getIGraphicBufferProducer();
+#else
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&mProducer, &consumer);
@@ -34,6 +40,7 @@
consumer->setDefaultBufferSize(100, 100);
mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/tests/benchmarks/Android.bp b/services/surfaceflinger/tests/benchmarks/Android.bp
new file mode 100644
index 0000000..1c47be34
--- /dev/null
+++ b/services/surfaceflinger/tests/benchmarks/Android.bp
@@ -0,0 +1,31 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+ name: "surfaceflinger_microbenchmarks",
+ srcs: [
+ ":libsurfaceflinger_mock_sources",
+ ":libsurfaceflinger_sources",
+ "*.cpp",
+ ],
+ defaults: [
+ "libsurfaceflinger_mocks_defaults",
+ "skia_renderengine_deps",
+ "surfaceflinger_defaults",
+ ],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ "libc++fs",
+ ],
+ header_libs: [
+ "libsurfaceflinger_mocks_headers",
+ "surfaceflinger_tests_common_headers",
+ ],
+}
diff --git a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
new file mode 100644
index 0000000..7641a45
--- /dev/null
+++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <optional>
+
+#include <benchmark/benchmark.h>
+
+#include <Client.h> // temporarily needed for LayerCreationArgs
+#include <FrontEnd/LayerCreationArgs.h>
+#include <FrontEnd/LayerLifecycleManager.h>
+#include <LayerLifecycleManagerHelper.h>
+
+namespace android::surfaceflinger {
+
+namespace {
+
+using namespace android::surfaceflinger::frontend;
+
+static void addRemoveLayers(benchmark::State& state) {
+ LayerLifecycleManager lifecycleManager;
+ for (auto _ : state) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
+ layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(2));
+ layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({{1, "1"}, {2, "2"}, {3, "3"}});
+ lifecycleManager.commitChanges();
+ }
+}
+BENCHMARK(addRemoveLayers);
+
+static void updateClientStates(benchmark::State& state) {
+ LayerLifecycleManager lifecycleManager;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ auto& transactionState = transactions.back().states.front();
+ transactionState.state.what = layer_state_t::eColorChanged;
+ transactionState.state.color.rgb = {0.f, 0.f, 0.f};
+ transactionState.layerId = 1;
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.commitChanges();
+ int i = 0;
+ for (auto s : state) {
+ if (i++ % 100 == 0) i = 0;
+ transactionState.state.color.b = static_cast<float>(i / 100.f);
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.commitChanges();
+ }
+}
+BENCHMARK(updateClientStates);
+
+static void updateClientStatesNoChanges(benchmark::State& state) {
+ LayerLifecycleManager lifecycleManager;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ auto& transactionState = transactions.back().states.front();
+ transactionState.state.what = layer_state_t::eColorChanged;
+ transactionState.state.color.rgb = {0.f, 0.f, 0.f};
+ transactionState.layerId = 1;
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.commitChanges();
+ for (auto _ : state) {
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.commitChanges();
+ }
+}
+BENCHMARK(updateClientStatesNoChanges);
+
+} // namespace
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/tests/benchmarks/LocklessQueue_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LocklessQueue_benchmarks.cpp
new file mode 100644
index 0000000..60bd58a
--- /dev/null
+++ b/services/surfaceflinger/tests/benchmarks/LocklessQueue_benchmarks.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <optional>
+
+#include <benchmark/benchmark.h>
+
+#include <LocklessQueue.h>
+
+namespace android::surfaceflinger {
+
+namespace {
+static void pushPop(benchmark::State& state) {
+ LocklessQueue<std::vector<uint32_t>> queue;
+ for (auto _ : state) {
+ queue.push({10, 5});
+ std::vector<uint32_t> poppedValue = *queue.pop();
+ benchmark::DoNotOptimize(poppedValue);
+ }
+}
+BENCHMARK(pushPop);
+
+} // namespace
+} // namespace android::surfaceflinger
diff --git a/libs/tracing_perfetto/include/trace_result.h b/services/surfaceflinger/tests/benchmarks/main.cpp
similarity index 70%
copy from libs/tracing_perfetto/include/trace_result.h
copy to services/surfaceflinger/tests/benchmarks/main.cpp
index f7581fc..685c7c6 100644
--- a/libs/tracing_perfetto/include/trace_result.h
+++ b/services/surfaceflinger/tests/benchmarks/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 The Android Open Source Project
+ * 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.
@@ -14,17 +14,5 @@
* limitations under the License.
*/
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
-
-namespace tracing_perfetto {
-
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
-};
-
-}
-
-#endif // TRACE_RESULT_H
+#include <benchmark/benchmark.h>
+BENCHMARK_MAIN();
diff --git a/services/surfaceflinger/tests/common/Android.bp b/services/surfaceflinger/tests/common/Android.bp
new file mode 100644
index 0000000..2dfa8af
--- /dev/null
+++ b/services/surfaceflinger/tests/common/Android.bp
@@ -0,0 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_headers {
+ name: "surfaceflinger_tests_common_headers",
+ export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
new file mode 100644
index 0000000..ae380ad
--- /dev/null
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -0,0 +1,539 @@
+/*
+ * 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 <gui/fake/BufferData.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <ui/ShadowSettings.h>
+
+#include <Client.h> // temporarily needed for LayerCreationArgs
+#include <FrontEnd/LayerCreationArgs.h>
+#include <FrontEnd/LayerHierarchy.h>
+#include <FrontEnd/LayerLifecycleManager.h>
+#include <FrontEnd/LayerSnapshotBuilder.h>
+#include <Layer.h> // needed for framerate
+
+namespace android::surfaceflinger::frontend {
+
+class LayerLifecycleManagerHelper {
+public:
+ LayerLifecycleManagerHelper(LayerLifecycleManager& layerLifecycleManager)
+ : mLifecycleManager(layerLifecycleManager) {}
+ ~LayerLifecycleManagerHelper() = default;
+
+ static LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+ uint32_t layerIdToMirror) {
+ LayerCreationArgs args(std::make_optional(id));
+ args.name = "testlayer";
+ args.addToRoot = canBeRoot;
+ args.parentId = parentId;
+ args.layerIdToMirror = layerIdToMirror;
+ return args;
+ }
+
+ static LayerCreationArgs createDisplayMirrorArgs(uint32_t id,
+ ui::LayerStack layerStackToMirror) {
+ LayerCreationArgs args(std::make_optional(id));
+ args.name = "testlayer";
+ args.addToRoot = true;
+ args.layerStackToMirror = layerStackToMirror;
+ return args;
+ }
+
+ static std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
+ return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
+ /*parent=*/UNASSIGNED_LAYER_ID,
+ /*mirror=*/UNASSIGNED_LAYER_ID));
+ }
+
+ static std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
+ return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
+ parentId,
+ /*mirror=*/UNASSIGNED_LAYER_ID));
+ }
+
+ static std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.z = z;
+ return transactions;
+ }
+
+ void createRootLayer(uint32_t id) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+ /*mirror=*/UNASSIGNED_LAYER_ID)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createRootLayerWithUid(uint32_t id, gui::Uid uid) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ auto args = createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+ /*mirror=*/UNASSIGNED_LAYER_ID);
+ args.ownerUid = uid.val();
+ layers.emplace_back(std::make_unique<RequestedLayerState>(args));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createDisplayMirrorArgs(/*id=*/id, layerStack)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+ /*mirror=*/UNASSIGNED_LAYER_ID)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().parentId = newParentId;
+ transactions.back().states.front().state.what = layer_state_t::eReparent;
+ transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
+ transactions.back().states.front().layerId = id;
+ return transactions;
+ }
+
+ void reparentLayer(uint32_t id, uint32_t newParentId) {
+ mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
+ }
+
+ std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().relativeParentId = relativeParentId;
+ transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ transactions.back().states.front().layerId = id;
+ return transactions;
+ }
+
+ void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+ mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
+ }
+
+ void removeRelativeZ(uint32_t id) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setPosition(uint32_t id, float x, float y) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
+ transactions.back().states.front().state.x = x;
+ transactions.back().states.front().state.y = y;
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+ /*mirror=*/layerIdToMirror)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void updateBackgroundColor(uint32_t id, half alpha) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.bgColor.a = alpha;
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
+
+ void setZ(uint32_t id, int32_t z) {
+ mLifecycleManager.applyTransactions(setZTransaction(id, z));
+ }
+
+ void setCrop(uint32_t id, const Rect& crop) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eCropChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.crop = crop;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
+ transactions.back().states.front().state.flags = flags;
+ transactions.back().states.front().state.mask = mask;
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setAlpha(uint32_t id, float alpha) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.color.a = static_cast<half>(alpha);
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void hideLayer(uint32_t id) {
+ setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+ }
+
+ void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
+
+ void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eColorChanged;
+ transactions.back().states.front().state.color.rgb = rgb;
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setLayerStack(uint32_t id, int32_t layerStack) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setTouchableRegion(uint32_t id, Region region) {
+ 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 = id;
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->touchableRegion = region;
+ inputInfo->token = sp<BBinder>::make();
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
+ 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 = id;
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ if (!inputInfo->token) {
+ inputInfo->token = sp<BBinder>::make();
+ }
+ configureInput(*inputInfo);
+
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
+ bool replaceTouchableRegionWithCrop) {
+ 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 = id;
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->touchableRegion = region;
+ inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
+ transactions.back().states.front().touchCropId = touchCropId;
+
+ inputInfo->token = sp<BBinder>::make();
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateSelectionPriority = priority;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRate = frameRate;
+ transactions.back().states.front().state.frameRateCompatibility = compatibility;
+ transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRate(uint32_t id, Layer::FrameRate framerate) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRate = framerate.vote.rate.getValue();
+ transactions.back().states.front().state.frameRateCompatibility = 0;
+ transactions.back().states.front().state.changeFrameRateStrategy = 0;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateCategory = frameRateCategory;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eFrameRateSelectionStrategyChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateSelectionStrategy = strategy;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.defaultFrameRateCompatibility =
+ defaultFrameRateCompatibility;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setRoundedCorners(uint32_t id, float radius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.cornerRadius = radius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBufferChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().externalTexture = texture;
+ transactions.back().states.front().state.bufferData =
+ std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(),
+ texture->getHeight(), texture->getPixelFormat(),
+ texture->getUsage());
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setBuffer(uint32_t id) {
+ static uint64_t sBufferId = 1;
+ setBuffer(id,
+ std::make_shared<renderengine::mock::
+ FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ sBufferId++,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED /*usage*/));
+ }
+
+ void setFrontBuffer(uint32_t id) {
+ static uint64_t sBufferId = 1;
+ setBuffer(id,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(
+ 1U /*width*/, 1U /*height*/, sBufferId++, HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/));
+ }
+
+ void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.bufferCrop = bufferCrop;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setDamageRegion(uint32_t id, const Region& damageRegion) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setDataspace(uint32_t id, ui::Dataspace dataspace) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.dataspace = dataspace;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
+ layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.matrix = matrix;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setShadowRadius(uint32_t id, float shadowRadius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.shadowRadius = shadowRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.trustedOverlay = trustedOverlay;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.dropInputMode = dropInputMode;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setGameMode(uint32_t id, gui::GameMode gameMode) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+ transactions.back().states.front().state.metadata = LayerMetadata();
+ transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE,
+ static_cast<int32_t>(gameMode));
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setEdgeExtensionEffect(uint32_t id, int edge) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.what |= layer_state_t::eEdgeExtensionChanged;
+ transactions.back().states.front().state.edgeExtensionParameters =
+ gui::EdgeExtensionParameters();
+ transactions.back().states.front().state.edgeExtensionParameters.extendLeft = edge & LEFT;
+ transactions.back().states.front().state.edgeExtensionParameters.extendRight = edge & RIGHT;
+ transactions.back().states.front().state.edgeExtensionParameters.extendTop = edge & TOP;
+ transactions.back().states.front().state.edgeExtensionParameters.extendBottom =
+ edge & BOTTOM;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+private:
+ LayerLifecycleManager& mLifecycleManager;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 98d5754..f1bd87c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -58,6 +58,7 @@
],
test_suites: ["device-tests"],
static_libs: ["libc++fs"],
+ header_libs: ["surfaceflinger_tests_common_headers"],
srcs: [
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
@@ -79,20 +80,16 @@
"FpsTest.cpp",
"FramebufferSurfaceTest.cpp",
"FrameRateOverrideMappingsTest.cpp",
- "FrameRateSelectionPriorityTest.cpp",
- "FrameRateSelectionStrategyTest.cpp",
"FrameTimelineTest.cpp",
- "GameModeTest.cpp",
"HWComposerTest.cpp",
+ "JankTrackerTest.cpp",
"OneShotTimerTest.cpp",
- "LayerHistoryTest.cpp",
"LayerHistoryIntegrationTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
"LayerHierarchyTest.cpp",
"LayerLifecycleManagerTest.cpp",
"LayerSnapshotTest.cpp",
- "LayerTest.cpp",
"LayerTestUtils.cpp",
"MessageQueueTest.cpp",
"PowerAdvisorTest.cpp",
@@ -115,9 +112,7 @@
"SurfaceFlinger_SetDisplayStateTest.cpp",
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
- "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
"SchedulerTest.cpp",
- "SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
@@ -151,6 +146,7 @@
"android.hardware.power-ndk_static",
"librenderengine_deps",
"libsurfaceflinger_common_test_deps",
+ "libsurfaceflinger_proto_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
@@ -169,7 +165,6 @@
"libframetimeline",
"libgmock",
"libgui_mocks",
- "liblayers_proto",
"libperfetto_client_experimental",
"librenderengine",
"librenderengine_mocks",
@@ -208,6 +203,7 @@
"libsync",
"libui",
"libutils",
+ "libtracing_perfetto",
],
header_libs: [
"android.hardware.graphics.composer3-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index cdd77fe..23d3c16 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -15,7 +15,6 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#include "renderengine/ExternalTexture.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
@@ -31,6 +30,7 @@
#include <gui/IProducerListener.h>
#include <gui/LayerMetadata.h>
#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
#include <renderengine/mock/FakeExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
#include <system/window.h>
@@ -149,7 +149,6 @@
sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
sp<compositionengine::mock::DisplaySurface>::make();
sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
- std::vector<sp<Layer>> mAuxiliaryLayers;
sp<GraphicBuffer> mBuffer =
sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
@@ -194,6 +193,7 @@
template <typename LayerCase>
void CompositionTest::captureScreenComposition() {
LayerCase::setupForScreenCapture(this);
+ mFlinger.commit();
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
constexpr bool regionSampling = false;
@@ -204,13 +204,8 @@
RenderArea::Options::CAPTURE_SECURE_LAYERS |
RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION);
- auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
- return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
- CaptureArgs::UNSET_UID, {}, visitor);
- };
-
- // TODO: Use SurfaceFlinger::getLayerSnapshotsForScreenshots instead of this legacy function
- auto getLayerSnapshotsFn = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ auto getLayerSnapshotsFn = mFlinger.getLayerSnapshotsForScreenshotsFn(mDisplay->getLayerStack(),
+ CaptureArgs::UNSET_UID);
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -462,7 +457,7 @@
static constexpr IComposerClient::BlendMode BLENDMODE =
IComposerClient::BlendMode::PREMULTIPLIED;
- static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) {
+ static void setupLatchedBuffer(CompositionTest* test, frontend::RequestedLayerState& layer) {
Mock::VerifyAndClear(test->mRenderEngine);
const auto buffer = std::make_shared<
@@ -472,21 +467,15 @@
LayerProperties::FORMAT,
LayerProperties::USAGE |
GraphicBuffer::USAGE_HW_TEXTURE);
-
- auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
- layerDrawingState.buffer = buffer;
- layerDrawingState.acquireFence = Fence::NO_FENCE;
- layerDrawingState.dataspace = ui::Dataspace::UNKNOWN;
- layer->setSurfaceDamageRegion(
- Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
-
- bool ignoredRecomputeVisibleRegions;
- layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
+ layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layer.externalTexture = buffer;
+ layer.bufferData->acquireFence = Fence::NO_FENCE;
+ layer.dataspace = ui::Dataspace::UNKNOWN;
+ layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH));
Mock::VerifyAndClear(test->mRenderEngine);
}
- static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+ static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
setupLatchedBuffer(test, layer);
}
@@ -670,14 +659,12 @@
using Base = BaseLayerProperties<SidebandLayerProperties>;
static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
- static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+ static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- test->mFlinger.setLayerSidebandStream(layer, stream);
- auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.crop =
- Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
+ layer.sidebandStream = stream;
+ layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
}
static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -755,17 +742,17 @@
struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
using Base = BaseLayerProperties<CursorLayerProperties>;
- static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+ static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
Base::setupLayerState(test, layer);
- test->mFlinger.setLayerPotentialCursor(layer, true);
+ layer.potentialCursor = true;
}
};
struct NoLayerVariant {
- using FlingerLayerType = sp<Layer>;
-
- static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
- static void injectLayer(CompositionTest*, FlingerLayerType) {}
+ static frontend::RequestedLayerState createLayer(CompositionTest*) {
+ return {LayerCreationArgs()};
+ }
+ static void injectLayer(CompositionTest*, frontend::RequestedLayerState&) {}
static void cleanupInjectedLayers(CompositionTest*) {}
static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
@@ -775,10 +762,10 @@
template <typename LayerProperties>
struct BaseLayerVariant {
template <typename L, typename F>
- static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
+ static frontend::RequestedLayerState createLayerWithFactory(CompositionTest* test, F factory) {
EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0);
- sp<L> layer = factory();
+ auto layer = factory();
// Layer should be registered with scheduler.
EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize());
@@ -792,27 +779,26 @@
return layer;
}
- template <typename L>
- static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
- auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.layerStack = LAYER_STACK;
- layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
- LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
- layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
+ static void initLayerDrawingStateAndComputeBounds(CompositionTest* test,
+ frontend::RequestedLayerState& layer) {
+ layer.layerStack = LAYER_STACK;
+ layer.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+ LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
}
- static void injectLayer(CompositionTest* test, sp<Layer> layer) {
+ static void injectLayer(CompositionTest* test, frontend::RequestedLayerState& layer) {
EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
.WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
-
+ auto legacyLayer = test->mFlinger.getLegacyLayer(layer.id);
auto outputLayer = test->mDisplay->getCompositionDisplay()->injectOutputLayerForTest(
- layer->getCompositionEngineLayerFE());
+ legacyLayer->getCompositionEngineLayerFE({.id = layer.id}));
outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
Mock::VerifyAndClear(test->mComposer);
- test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+ auto layerCopy = std::make_unique<frontend::RequestedLayerState>(layer);
+ test->mFlinger.addLayer(layerCopy);
test->mFlinger.mutableVisibleRegionsDirty() = true;
}
@@ -820,10 +806,9 @@
EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
.WillOnce(Return(Error::NONE));
+ test->mFlinger.destroyAllLayerHandles();
test->mDisplay->getCompositionDisplay()->clearOutputLayers();
- test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
test->mFlinger.mutablePreviouslyComposedLayers().clear();
-
// Layer should be unregistered with scheduler.
test->mFlinger.commit();
EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize());
@@ -833,17 +818,17 @@
template <typename LayerProperties>
struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
using Base = BaseLayerVariant<LayerProperties>;
- using FlingerLayerType = sp<Layer>;
-
- static FlingerLayerType createLayer(CompositionTest* test) {
- FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
- return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(),
- "test-layer", LayerProperties::LAYER_FLAGS,
- LayerMetadata()));
+ static frontend::RequestedLayerState createLayer(CompositionTest* test) {
+ frontend::RequestedLayerState layer = Base::template createLayerWithFactory<
+ frontend::RequestedLayerState>(test, [test]() {
+ auto args = LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
+ LayerProperties::LAYER_FLAGS, LayerMetadata());
+ auto legacyLayer = sp<Layer>::make(args);
+ test->mFlinger.injectLegacyLayer(legacyLayer);
+ return frontend::RequestedLayerState(args);
});
- auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
return layer;
}
@@ -869,13 +854,15 @@
template <typename LayerProperties>
struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
using Base = BaseLayerVariant<LayerProperties>;
- using FlingerLayerType = sp<Layer>;
- static FlingerLayerType createLayer(CompositionTest* test) {
- FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+ static frontend::RequestedLayerState createLayer(CompositionTest* test) {
+ frontend::RequestedLayerState layer = Base::template createLayerWithFactory<
+ frontend::RequestedLayerState>(test, [test]() {
LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
LayerProperties::LAYER_FLAGS, LayerMetadata());
- return sp<Layer>::make(args);
+ auto legacyLayer = sp<Layer>::make(args);
+ test->mFlinger.injectLegacyLayer(legacyLayer);
+ return frontend::RequestedLayerState(args);
});
LayerProperties::setupLayerState(test, layer);
@@ -917,13 +904,14 @@
template <typename LayerProperties>
struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
using Base = BaseLayerVariant<LayerProperties>;
- using FlingerLayerType = sp<Layer>;
- static FlingerLayerType createLayer(CompositionTest* test) {
+ static frontend::RequestedLayerState createLayer(CompositionTest* test) {
LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
LayerProperties::LAYER_FLAGS, LayerMetadata());
- FlingerLayerType layer = sp<Layer>::make(args);
- Base::template initLayerDrawingStateAndComputeBounds(test, layer);
+ sp<Layer> legacyLayer = sp<Layer>::make(args);
+ test->mFlinger.injectLegacyLayer(legacyLayer);
+ frontend::RequestedLayerState layer(args);
+ Base::initLayerDrawingStateAndComputeBounds(test, layer);
return layer;
}
};
@@ -931,29 +919,19 @@
template <typename LayerVariant, typename ParentLayerVariant>
struct ChildLayerVariant : public LayerVariant {
using Base = LayerVariant;
- using FlingerLayerType = typename LayerVariant::FlingerLayerType;
using ParentBase = ParentLayerVariant;
- static FlingerLayerType createLayer(CompositionTest* test) {
+ static frontend::RequestedLayerState createLayer(CompositionTest* test) {
// Need to create child layer first. Otherwise layer history size will be 2.
- FlingerLayerType layer = Base::createLayer(test);
-
- typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test);
- parentLayer->addChild(layer);
- test->mFlinger.setLayerDrawingParent(layer, parentLayer);
-
- test->mAuxiliaryLayers.push_back(parentLayer);
-
+ frontend::RequestedLayerState layer = Base::createLayer(test);
+ frontend::RequestedLayerState parentLayer = ParentBase::createLayer(test);
+ layer.parentId = parentLayer.id;
+ auto layerCopy = std::make_unique<frontend::RequestedLayerState>(parentLayer);
+ test->mFlinger.addLayer(layerCopy);
return layer;
}
- static void cleanupInjectedLayers(CompositionTest* test) {
- // Clear auxiliary layers first so that child layer can be successfully destroyed in the
- // following call.
- test->mAuxiliaryLayers.clear();
-
- Base::cleanupInjectedLayers(test);
- }
+ static void cleanupInjectedLayers(CompositionTest* test) { Base::cleanupInjectedLayers(test); }
};
/* ------------------------------------------------------------------------
@@ -1016,7 +994,7 @@
*/
struct CompositionResultBaseVariant {
- static void setupLayerState(CompositionTest*, sp<Layer>) {}
+ static void setupLayerState(CompositionTest*, frontend::RequestedLayerState&) {}
template <typename Case>
static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
@@ -1056,9 +1034,8 @@
};
struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
- static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
- const auto outputLayer =
- TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay);
+ static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
+ const auto outputLayer = test->mFlinger.findOutputLayerForDisplay(layer.id, test->mDisplay);
LOG_FATAL_IF(!outputLayer);
outputLayer->editState().forceClientComposition = true;
}
@@ -1079,7 +1056,7 @@
};
struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant {
- static void setupLayerState(CompositionTest* test, sp<Layer>) {
+ static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState&) {
test->mFlinger.mutableDebugDisableHWC() = true;
}
@@ -1099,7 +1076,7 @@
};
struct EmptyScreenshotResultVariant {
- static void setupLayerState(CompositionTest*, sp<Layer>) {}
+ static void setupLayerState(CompositionTest*, frontend::RequestedLayerState&) {}
template <typename Case>
static void setupCallExpectations(CompositionTest*) {}
@@ -1365,28 +1342,6 @@
* Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display
*/
-TEST_F(CompositionTest,
- HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<CompositionCase<
- InsecureDisplaySetupVariant,
- ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
- ContainerLayerVariant<SecureLayerProperties>>,
- KeepCompositionTypeVariant<
- aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
-}
-
-TEST_F(CompositionTest,
- HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<CompositionCase<
- InsecureDisplaySetupVariant,
- ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
- ContainerLayerVariant<SecureLayerProperties>>,
- KeepCompositionTypeVariant<
- aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
-}
-
TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
captureScreenComposition<
CompositionCase<InsecureDisplaySetupVariant,
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index fa31643..9b10c94 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -64,17 +64,6 @@
void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) {
LOG_ALWAYS_FATAL_IF(mFlinger.scheduler());
-
- EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(
- sp<EventThreadConnection>::make(mEventThread, mock::EventThread::kCallingUid)));
-
- EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread,
- mock::EventThread::kCallingUid)));
-
mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(),
std::unique_ptr<EventThread>(mEventThread),
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index f26336a..db3c0a1 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -498,9 +498,7 @@
constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
template <typename PhysicalDisplay, int width, int height,
- Secure secure = (PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal)
- ? Secure::TRUE
- : Secure::FALSE>
+ Secure secure = (PhysicalDisplay::SECURE) ? Secure::TRUE : Secure::FALSE>
struct PhysicalDisplayVariant
: DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, secure,
PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
@@ -515,16 +513,18 @@
struct PrimaryDisplay {
static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::Internal;
static constexpr Primary PRIMARY = Primary::TRUE;
+ static constexpr bool SECURE = true;
static constexpr uint8_t PORT = 255;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
};
-template <ui::DisplayConnectionType connectionType, bool hasIdentificationData>
+template <ui::DisplayConnectionType connectionType, bool hasIdentificationData, bool secure>
struct SecondaryDisplay {
static constexpr auto CONNECTION_TYPE = connectionType;
static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr bool SECURE = secure;
static constexpr uint8_t PORT = 254;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
@@ -533,9 +533,14 @@
: getExternalEdid;
};
+constexpr bool kSecure = true;
+constexpr bool kNonSecure = false;
+
+template <bool secure>
struct TertiaryDisplay {
static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr bool SECURE = secure;
static constexpr uint8_t PORT = 253;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
@@ -545,14 +550,26 @@
using InnerDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<true>, 1840, 2208>;
using OuterDisplayVariant =
- PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, true>, 1080,
- 2092>;
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal,
+ /*hasIdentificationData=*/true, kSecure>,
+ 1080, 2092>;
+using OuterDisplayNonSecureVariant =
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal,
+ /*hasIdentificationData=*/true, kNonSecure>,
+ 1080, 2092>;
using ExternalDisplayVariant =
- PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, false>, 1920,
- 1280>;
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
+ /*hasIdentificationData=*/false, kSecure>,
+ 1920, 1280>;
+using ExternalDisplayNonSecureVariant =
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
+ /*hasIdentificationData=*/false, kNonSecure>,
+ 1920, 1280>;
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200>;
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay<kSecure>, 1600, 1200>;
+using TertiaryDisplayNonSecureVariant =
+ PhysicalDisplayVariant<TertiaryDisplay<kNonSecure>, 1600, 1200>;
// A virtual display not supported by the HWC.
constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
@@ -750,10 +767,18 @@
Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
HdrNotSupportedVariant<ExternalDisplayVariant>,
NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
+using SimpleExternalDisplayNonSecureCase =
+ Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayNonSecureVariant>,
+ HdrNotSupportedVariant<ExternalDisplayNonSecureVariant>,
+ NoPerFrameMetadataSupportVariant<ExternalDisplayNonSecureVariant>>;
using SimpleTertiaryDisplayCase =
Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
HdrNotSupportedVariant<TertiaryDisplayVariant>,
NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
+using SimpleTertiaryDisplayNonSecureCase =
+ Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayNonSecureVariant>,
+ HdrNotSupportedVariant<TertiaryDisplayNonSecureVariant>,
+ NoPerFrameMetadataSupportVariant<TertiaryDisplayNonSecureVariant>>;
using NonHwcVirtualDisplayCase =
Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
deleted file mode 100644
index d30d5b8..0000000
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "Layer.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::_;
-using testing::DoAll;
-using testing::Mock;
-using testing::Return;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-/**
- * This class covers all the test that are related to refresh rate selection.
- */
-class RefreshRateSelectionTest : public testing::Test {
-public:
- RefreshRateSelectionTest();
- ~RefreshRateSelectionTest() override;
-
-protected:
- static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
- static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
- static constexpr uint32_t WIDTH = 100;
- static constexpr uint32_t HEIGHT = 100;
- static constexpr uint32_t LAYER_FLAGS = 0;
- static constexpr int32_t PRIORITY_UNSET = -1;
-
- sp<Layer> createBufferStateLayer();
- sp<Layer> createEffectLayer();
-
- void setParent(Layer* child, Layer* parent);
- void commitTransaction(Layer* layer);
-
- TestableSurfaceFlinger mFlinger;
-
- sp<Client> mClient;
- sp<Layer> mParent;
- sp<Layer> mChild;
- sp<Layer> mGrandChild;
-};
-
-RefreshRateSelectionTest::RefreshRateSelectionTest() {
- const ::testing::TestInfo* const test_info =
- ::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>());
-}
-
-RefreshRateSelectionTest::~RefreshRateSelectionTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
- LayerMetadata());
- return sp<Layer>::make(args);
-}
-
-sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
- return sp<Layer>::make(args);
-}
-
-void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
- child->setParent(sp<Layer>::fromExisting(parent));
-}
-
-void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
- layer->commitTransaction();
-}
-
-namespace {
-
-TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
- mParent = createBufferStateLayer();
- mChild = createBufferStateLayer();
- setParent(mChild.get(), mParent.get());
- mGrandChild = createBufferStateLayer();
- setParent(mGrandChild.get(), mChild.get());
-
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
- // Child has its own priority.
- mGrandChild->setFrameRateSelectionPriority(1);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Child inherits from his parent.
- mChild->setFrameRateSelectionPriority(1);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Grandchild inherits from his grand parent.
- mParent->setFrameRateSelectionPriority(1);
- commitTransaction(mParent.get());
- mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
-TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) {
- mParent = createEffectLayer();
- mChild = createEffectLayer();
- setParent(mChild.get(), mParent.get());
- mGrandChild = createEffectLayer();
- setParent(mGrandChild.get(), mChild.get());
-
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
- // Child has its own priority.
- mGrandChild->setFrameRateSelectionPriority(1);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Child inherits from his parent.
- mChild->setFrameRateSelectionPriority(1);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Grandchild inherits from his grand parent.
- mParent->setFrameRateSelectionPriority(1);
- commitTransaction(mParent.get());
- mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
deleted file mode 100644
index 5c742d7..0000000
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "Layer.h"
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::DoAll;
-using testing::Mock;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-using scheduler::LayerHistory;
-
-using FrameRate = Layer::FrameRate;
-using FrameRateCompatibility = Layer::FrameRateCompatibility;
-using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
-
-/**
- * This class tests the behaviour of Layer::setFrameRateSelectionStrategy.
- */
-class FrameRateSelectionStrategyTest : public BaseLayerTest {
-protected:
- const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_DEFAULT = FrameRate(Fps(), FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
-
- FrameRateSelectionStrategyTest();
-
- void addChild(sp<Layer> layer, sp<Layer> child);
- void removeChild(sp<Layer> layer, sp<Layer> child);
- void commitTransaction();
-
- std::vector<sp<Layer>> mLayers;
-};
-
-FrameRateSelectionStrategyTest::FrameRateSelectionStrategyTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-void FrameRateSelectionStrategyTest::addChild(sp<Layer> layer, sp<Layer> child) {
- layer->addChild(child);
-}
-
-void FrameRateSelectionStrategyTest::removeChild(sp<Layer> layer, sp<Layer> child) {
- layer->removeChild(child);
-}
-
-void FrameRateSelectionStrategyTest::commitTransaction() {
- for (auto layer : mLayers) {
- layer->commitTransaction();
- }
-}
-
-namespace {
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, FrameRateSelectionStrategyTest,
- testing::Values(std::make_shared<BufferStateLayerFactory>(),
- std::make_shared<EffectLayerFactory>()),
- PrintToStringParamName);
-
-TEST_P(FrameRateSelectionStrategyTest, SetAndGet) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- layer->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- parent->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- child1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- child2->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(layer1, layer2);
- addChild(layer2, layer3);
-
- layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
- layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- layer3->setFrameRate(FRAME_RATE_VOTE3.vote);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Propagate);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(layer1, layer2);
- addChild(layer2, layer3);
-
- layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
- layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_DEFAULT, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-
- layer1->setFrameRate(FRAME_RATE_DEFAULT.vote);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_TREE, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-}
-
-} // namespace
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index fae236d..08e4265 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -15,6 +15,8 @@
*/
#include <common/test/FlagUtils.h>
+#include "BackgroundExecutor.h"
+#include "Jank/JankTracker.h"
#include "com_android_graphics_surfaceflinger_flags.h"
#include "gmock/gmock-spec-builders.h"
#include "mock/MockTimeStats.h"
@@ -82,16 +84,22 @@
void SetUp() override {
constexpr bool kUseBootTimeClock = true;
+ constexpr bool kFilterFramesBeforeTraceStarts = false;
mTimeStats = std::make_shared<mock::TimeStats>();
mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
- kTestThresholds, !kUseBootTimeClock);
+ kTestThresholds, !kUseBootTimeClock,
+ kFilterFramesBeforeTraceStarts);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
maxTokens = mTokenManager->kMaxTokens;
+
+ JankTracker::clearAndStartCollectingAllJankDataForTesting();
}
+ void TearDown() override { JankTracker::clearAndStopCollectingAllJankDataForTesting(); }
+
// Each tracing session can be used for a single block of Start -> Stop.
static std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest() {
perfetto::TraceConfig cfg;
@@ -176,6 +184,16 @@
[&](FrameTimelineDataSource::TraceContext ctx) { ctx.Flush(); });
}
+ std::vector<gui::JankData> getLayerOneJankData() {
+ BackgroundExecutor::getLowPriorityInstance().flushQueue();
+ return JankTracker::getCollectedJankDataForTesting(sLayerIdOne);
+ }
+
+ std::vector<gui::JankData> getLayerTwoJankData() {
+ BackgroundExecutor::getLowPriorityInstance().flushQueue();
+ return JankTracker::getCollectedJankDataForTesting(sLayerIdTwo);
+ }
+
std::shared_ptr<mock::TimeStats> mTimeStats;
std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
impl::TokenManager* mTokenManager;
@@ -340,6 +358,9 @@
EXPECT_NE(surfaceFrame1->getJankSeverityType(), std::nullopt);
EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt);
EXPECT_NE(surfaceFrame2->getJankSeverityType(), std::nullopt);
+
+ EXPECT_EQ(getLayerOneJankData().size(), 1u);
+ EXPECT_EQ(getLayerTwoJankData().size(), 1u);
}
TEST_F(FrameTimelineTest, displayFrameSkippedComposition) {
@@ -447,6 +468,8 @@
// The window should have slided by 1 now and the previous 0th display frame
// should have been removed from the deque
EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true);
+
+ EXPECT_EQ(getLayerOneJankData().size(), *maxDisplayFrames);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
@@ -459,6 +482,16 @@
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceUnsignaled) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue",
+ /*isBuffer*/ true, sGameMode);
+ surfaceFrame->setActualQueueTime(123);
+ surfaceFrame->setAcquireFenceTime(Fence::SIGNAL_TIME_PENDING);
+ EXPECT_EQ(surfaceFrame->getActuals().endTime, 123);
+}
+
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
"acquireFenceAfterQueue",
@@ -576,6 +609,8 @@
presentFence1->signalForTest(70);
mFrameTimeline->setSfPresent(59, presentFence1);
+
+ EXPECT_EQ(getLayerOneJankData().size(), 0u);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
@@ -604,6 +639,10 @@
presentFence1->signalForTest(70);
mFrameTimeline->setSfPresent(62, presentFence1);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::SurfaceFlingerCpuDeadlineMissed);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) {
@@ -634,6 +673,10 @@
presentFence1->signalForTest(70);
mFrameTimeline->setSfPresent(59, presentFence1, gpuFence1);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::SurfaceFlingerGpuDeadlineMissed);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
@@ -662,6 +705,10 @@
mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::DisplayHAL);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -692,6 +739,10 @@
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::AppDeadlineMissed);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) {
@@ -722,6 +773,10 @@
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::SurfaceFlingerScheduling);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) {
@@ -752,6 +807,10 @@
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::PredictionError);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) {
@@ -783,6 +842,10 @@
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::BufferStuffing);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
@@ -815,6 +878,10 @@
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::AppDeadlineMissed);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) {
@@ -859,6 +926,10 @@
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::Unknown | JankType::AppDeadlineMissed);
}
/*
@@ -1790,6 +1861,10 @@
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::None);
EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::None);
+
+ auto jankData = getLayerOneJankData();
+ EXPECT_EQ(jankData.size(), 1u);
+ EXPECT_EQ(jankData[0].jankType, JankType::None);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
deleted file mode 100644
index 1b5c6e7..0000000
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-#include <gui/SurfaceComposerClient.h>
-#include <log/log.h>
-
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::_;
-using testing::Mock;
-using testing::Return;
-
-using gui::GameMode;
-using gui::LayerMetadata;
-
-class GameModeTest : public testing::Test {
-public:
- GameModeTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.setupMockScheduler();
- setupComposer();
- }
-
- ~GameModeTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-
- sp<Layer> createLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
- return sp<Layer>::make(args);
- }
-
- void setupComposer() {
- mComposer = new Hwc2::mock::Composer();
- mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-
- Mock::VerifyAndClear(mComposer);
- }
-
- // Mocks the behavior of applying a transaction from WMShell
- void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) {
- mLayerMetadata.setInt32(gui::METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
- layer->setMetadata(mLayerMetadata);
- layer->setGameModeForTree(gameMode);
- }
-
- TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
- client_cache_t mClientCache;
- LayerMetadata mLayerMetadata;
-};
-
-TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer1 = createLayer();
- sp<Layer> childLayer2 = createLayer();
- rootLayer->addChild(childLayer1);
- rootLayer->addChild(childLayer2);
- rootLayer->setGameModeForTree(GameMode::Performance);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
-}
-
-TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer = createLayer();
- rootLayer->setGameModeForTree(GameMode::Performance);
- rootLayer->addChild(childLayer);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
-}
-
-TEST_F(GameModeTest, RemoveChildResetsGameMode) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer = createLayer();
- rootLayer->setGameModeForTree(GameMode::Performance);
- rootLayer->addChild(childLayer);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
-
- rootLayer->removeChild(childLayer);
- EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported);
-}
-
-TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer1 = createLayer();
- sp<Layer> childLayer2 = createLayer();
- rootLayer->setGameModeForTree(GameMode::Standard);
- rootLayer->addChild(childLayer1);
-
- setGameModeMetadata(childLayer2, GameMode::Performance);
- rootLayer->addChild(childLayer2);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard);
- EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard);
- EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
-
- rootLayer->removeChild(childLayer2);
- EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 2cff2f2..e0753a3 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -58,6 +58,7 @@
using Hwc2::Config;
+using ::aidl::android::hardware::drm::HdcpLevels;
using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using hal::IComposerClient;
@@ -165,6 +166,7 @@
expectHotplugConnect(kHwcDisplayId);
const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
ASSERT_TRUE(info);
+ ASSERT_TRUE(info->preferredDetailedTimingDescriptor.has_value());
EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
@@ -178,6 +180,10 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
+ constexpr float kMmPerInch = 25.4f;
+ const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ const float expectedDpiX = (kWidth * kMmPerInch / size.width);
+ const float expectedDpiY = (kHeight * kMmPerInch / size.height);
EXPECT_CALL(*mHal,
getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
@@ -217,8 +223,13 @@
EXPECT_EQ(modes.front().height, kHeight);
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
- EXPECT_EQ(modes.front().dpiX, -1);
- EXPECT_EQ(modes.front().dpiY, -1);
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+ } else {
+ EXPECT_EQ(modes.front().dpiX, expectedDpiX);
+ EXPECT_EQ(modes.front().dpiY, expectedDpiY);
+ }
// Optional parameters are supported
constexpr int32_t kDpi = 320;
@@ -270,6 +281,10 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
+ constexpr float kMmPerInch = 25.4f;
+ const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ const float expectedDpiX = (kWidth * kMmPerInch / size.width);
+ const float expectedDpiY = (kHeight * kMmPerInch / size.height);
EXPECT_CALL(*mHal,
getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
@@ -309,8 +324,13 @@
EXPECT_EQ(modes.front().height, kHeight);
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
- EXPECT_EQ(modes.front().dpiX, -1);
- EXPECT_EQ(modes.front().dpiY, -1);
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+ } else {
+ EXPECT_EQ(modes.front().dpiX, expectedDpiX);
+ EXPECT_EQ(modes.front().dpiY, expectedDpiY);
+ }
// Optional parameters are supported
constexpr int32_t kDpi = 320;
@@ -360,6 +380,10 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
+ constexpr float kMmPerInch = 25.4f;
+ const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ const float expectedDpiX = (kWidth * kMmPerInch / size.width);
+ const float expectedDpiY = (kHeight * kMmPerInch / size.height);
const hal::VrrConfig vrrConfig =
hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
.notifyExpectedPresentConfig = hal::VrrConfig::
@@ -386,8 +410,13 @@
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
- EXPECT_EQ(modes.front().dpiX, -1);
- EXPECT_EQ(modes.front().dpiY, -1);
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+ } else {
+ EXPECT_EQ(modes.front().dpiX, expectedDpiX);
+ EXPECT_EQ(modes.front().dpiY, expectedDpiY);
+ }
// Supports optional dpi parameter
constexpr int32_t kDpi = 320;
@@ -454,6 +483,8 @@
MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId));
MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId));
MOCK_METHOD(void, onRefreshRateChangedDebug, (const RefreshRateChangedDebugData&), (override));
+ MOCK_METHOD(void, onComposerHalHdcpLevelsChanged, (hal::HWDisplayId, const HdcpLevels&),
+ (override));
};
struct HWComposerSetCallbackTest : HWComposerTest {
diff --git a/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
new file mode 100644
index 0000000..2941a14
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+#include <android/gui/BnJankListener.h>
+#include <binder/IInterface.h>
+#include "BackgroundExecutor.h"
+#include "Jank/JankTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace {
+
+using namespace testing;
+
+class MockJankListener : public gui::BnJankListener {
+public:
+ MockJankListener() = default;
+ ~MockJankListener() override = default;
+
+ MOCK_METHOD(binder::Status, onJankData, (const std::vector<gui::JankData>& jankData),
+ (override));
+};
+
+} // anonymous namespace
+
+class JankTrackerTest : public Test {
+public:
+ JankTrackerTest() {}
+
+ void SetUp() override { mListener = sp<StrictMock<MockJankListener>>::make(); }
+
+ void addJankListener(int32_t layerId) {
+ JankTracker::addJankListener(layerId, IInterface::asBinder(mListener));
+ }
+
+ void removeJankListener(int32_t layerId, int64_t after) {
+ JankTracker::removeJankListener(layerId, IInterface::asBinder(mListener), after);
+ }
+
+ void addJankData(int32_t layerId, int jankType) {
+ gui::JankData data;
+ data.frameVsyncId = mVsyncId++;
+ data.jankType = jankType;
+ data.frameIntervalNs = 8333333;
+ JankTracker::onJankData(layerId, data);
+ }
+
+ void flushBackgroundThread() { BackgroundExecutor::getLowPriorityInstance().flushQueue(); }
+
+ size_t listenerCount() { return JankTracker::sListenerCount; }
+
+ std::vector<gui::JankData> getCollectedJankData(int32_t layerId) {
+ return JankTracker::getCollectedJankDataForTesting(layerId);
+ }
+
+ sp<StrictMock<MockJankListener>> mListener = nullptr;
+ int64_t mVsyncId = 1000;
+};
+
+TEST_F(JankTrackerTest, jankDataIsTrackedAndPropagated) {
+ ASSERT_EQ(listenerCount(), 0u);
+
+ EXPECT_CALL(*mListener.get(), onJankData(SizeIs(3)))
+ .WillOnce([](const std::vector<gui::JankData>& jankData) {
+ EXPECT_EQ(jankData[0].frameVsyncId, 1000);
+ EXPECT_EQ(jankData[0].jankType, 1);
+ EXPECT_EQ(jankData[0].frameIntervalNs, 8333333);
+
+ EXPECT_EQ(jankData[1].frameVsyncId, 1001);
+ EXPECT_EQ(jankData[1].jankType, 2);
+ EXPECT_EQ(jankData[1].frameIntervalNs, 8333333);
+
+ EXPECT_EQ(jankData[2].frameVsyncId, 1002);
+ EXPECT_EQ(jankData[2].jankType, 3);
+ EXPECT_EQ(jankData[2].frameIntervalNs, 8333333);
+ return binder::Status::ok();
+ });
+ EXPECT_CALL(*mListener.get(), onJankData(SizeIs(2)))
+ .WillOnce([](const std::vector<gui::JankData>& jankData) {
+ EXPECT_EQ(jankData[0].frameVsyncId, 1003);
+ EXPECT_EQ(jankData[0].jankType, 4);
+ EXPECT_EQ(jankData[0].frameIntervalNs, 8333333);
+
+ EXPECT_EQ(jankData[1].frameVsyncId, 1004);
+ EXPECT_EQ(jankData[1].jankType, 5);
+ EXPECT_EQ(jankData[1].frameIntervalNs, 8333333);
+
+ return binder::Status::ok();
+ });
+
+ addJankListener(123);
+ addJankData(123, 1);
+ addJankData(123, 2);
+ addJankData(123, 3);
+ JankTracker::flushJankData(123);
+ addJankData(123, 4);
+ removeJankListener(123, mVsyncId);
+ addJankData(123, 5);
+ JankTracker::flushJankData(123);
+ addJankData(123, 6);
+ JankTracker::flushJankData(123);
+ removeJankListener(123, 0);
+
+ flushBackgroundThread();
+}
+
+TEST_F(JankTrackerTest, jankDataIsAutomaticallyFlushedInBatches) {
+ ASSERT_EQ(listenerCount(), 0u);
+
+ // needs to be larger than kJankDataBatchSize in JankTracker.cpp.
+ constexpr size_t kNumberOfJankDataToSend = 234;
+
+ size_t jankDataReceived = 0;
+ size_t numBatchesReceived = 0;
+
+ EXPECT_CALL(*mListener.get(), onJankData(_))
+ .WillRepeatedly([&](const std::vector<gui::JankData>& jankData) {
+ jankDataReceived += jankData.size();
+ numBatchesReceived++;
+ return binder::Status::ok();
+ });
+
+ addJankListener(123);
+ for (size_t i = 0; i < kNumberOfJankDataToSend; i++) {
+ addJankData(123, 0);
+ }
+
+ flushBackgroundThread();
+ // Check that we got some data, without explicitly flushing.
+ EXPECT_GT(jankDataReceived, 0u);
+ EXPECT_GT(numBatchesReceived, 0u);
+ EXPECT_LT(numBatchesReceived, jankDataReceived); // batches should be > size 1.
+
+ removeJankListener(123, 0);
+ JankTracker::flushJankData(123);
+ flushBackgroundThread();
+ EXPECT_EQ(jankDataReceived, kNumberOfJankDataToSend);
+}
+
+TEST_F(JankTrackerTest, jankListenerIsRemovedWhenReturningNullError) {
+ ASSERT_EQ(listenerCount(), 0u);
+
+ EXPECT_CALL(*mListener.get(), onJankData(SizeIs(3)))
+ .WillOnce(Return(binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER)));
+
+ addJankListener(123);
+ addJankData(123, 1);
+ addJankData(123, 2);
+ addJankData(123, 3);
+ JankTracker::flushJankData(123);
+ addJankData(123, 4);
+ addJankData(123, 5);
+ JankTracker::flushJankData(123);
+ flushBackgroundThread();
+
+ EXPECT_EQ(listenerCount(), 0u);
+}
+
+TEST_F(JankTrackerTest, jankDataIsDroppedIfNobodyIsListening) {
+ ASSERT_EQ(listenerCount(), 0u);
+
+ addJankData(123, 1);
+ addJankData(123, 2);
+ addJankData(123, 3);
+ flushBackgroundThread();
+
+ EXPECT_EQ(getCollectedJankData(123).size(), 0u);
+}
+
+TEST_F(JankTrackerTest, listenerCountTracksRegistrations) {
+ ASSERT_EQ(listenerCount(), 0u);
+
+ addJankListener(123);
+ addJankListener(456);
+ flushBackgroundThread();
+ EXPECT_EQ(listenerCount(), 2u);
+
+ removeJankListener(123, 0);
+ JankTracker::flushJankData(123);
+ removeJankListener(456, 0);
+ JankTracker::flushJankData(456);
+ flushBackgroundThread();
+ EXPECT_EQ(listenerCount(), 0u);
+}
+
+TEST_F(JankTrackerTest, listenerCountIsAccurateOnDuplicateRegistration) {
+ ASSERT_EQ(listenerCount(), 0u);
+
+ addJankListener(123);
+ addJankListener(123);
+ flushBackgroundThread();
+ EXPECT_EQ(listenerCount(), 1u);
+
+ removeJankListener(123, 0);
+ JankTracker::flushJankData(123);
+ flushBackgroundThread();
+ EXPECT_EQ(listenerCount(), 0u);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 8b3303c..37cda3e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -25,13 +25,15 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
+#include "LayerLifecycleManagerHelper.h"
+
#include "FrontEnd/LayerSnapshotBuilder.h"
namespace android::surfaceflinger::frontend {
-class LayerHierarchyTestBase : public testing::Test {
+class LayerHierarchyTestBase : public testing::Test, public LayerLifecycleManagerHelper {
protected:
- LayerHierarchyTestBase() {
+ LayerHierarchyTestBase() : LayerLifecycleManagerHelper(mLifecycleManager) {
// tree with 3 levels of children
// ROOT
// ├── 1
@@ -55,24 +57,6 @@
createLayer(1221, 122);
}
- LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
- uint32_t layerIdToMirror) {
- LayerCreationArgs args(std::make_optional(id));
- args.name = "testlayer";
- args.addToRoot = canBeRoot;
- args.parentId = parentId;
- args.layerIdToMirror = layerIdToMirror;
- return args;
- }
-
- LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) {
- LayerCreationArgs args(std::make_optional(id));
- args.name = "testlayer";
- args.addToRoot = true;
- args.layerStackToMirror = layerStackToMirror;
- return args;
- }
-
std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
std::vector<uint32_t> layerIds;
hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
@@ -94,98 +78,6 @@
return layerIds;
}
- virtual void createRootLayer(uint32_t id) {
- std::vector<std::unique_ptr<RequestedLayerState>> layers;
- layers.emplace_back(std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
- /*mirror=*/UNASSIGNED_LAYER_ID)));
- mLifecycleManager.addLayers(std::move(layers));
- }
-
- void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
- std::vector<std::unique_ptr<RequestedLayerState>> layers;
- layers.emplace_back(std::make_unique<RequestedLayerState>(
- createDisplayMirrorArgs(/*id=*/id, layerStack)));
- mLifecycleManager.addLayers(std::move(layers));
- }
-
- virtual void createLayer(uint32_t id, uint32_t parentId) {
- std::vector<std::unique_ptr<RequestedLayerState>> layers;
- layers.emplace_back(std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
- /*mirror=*/UNASSIGNED_LAYER_ID)));
- mLifecycleManager.addLayers(std::move(layers));
- }
-
- std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().parentId = newParentId;
- transactions.back().states.front().state.what = layer_state_t::eReparent;
- transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
- transactions.back().states.front().layerId = id;
- return transactions;
- }
-
- void reparentLayer(uint32_t id, uint32_t newParentId) {
- mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
- }
-
- std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().relativeParentId = relativeParentId;
- transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
- transactions.back().states.front().layerId = id;
- return transactions;
- }
-
- void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
- mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
- }
-
- void removeRelativeZ(uint32_t id) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
- transactions.back().states.front().layerId = id;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setPosition(uint32_t id, float x, float y) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
- transactions.back().states.front().state.x = x;
- transactions.back().states.front().state.y = y;
- transactions.back().states.front().layerId = id;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
- std::vector<std::unique_ptr<RequestedLayerState>> layers;
- layers.emplace_back(std::make_unique<RequestedLayerState>(
- createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
- /*mirror=*/layerIdToMirror)));
- mLifecycleManager.addLayers(std::move(layers));
- }
-
- void updateBackgroundColor(uint32_t id, half alpha) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- transactions.back().states.front().state.bgColor.a = alpha;
- transactions.back().states.front().layerId = id;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
-
void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
hierarchyBuilder.update(mLifecycleManager);
mLifecycleManager.commitChanges();
@@ -201,321 +93,6 @@
mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
}
- std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.z = z;
- return transactions;
- }
-
- void setZ(uint32_t id, int32_t z) {
- mLifecycleManager.applyTransactions(setZTransaction(id, z));
- }
-
- void setCrop(uint32_t id, const Rect& crop) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eCropChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.crop = crop;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
- transactions.back().states.front().state.flags = flags;
- transactions.back().states.front().state.mask = mask;
- transactions.back().states.front().layerId = id;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setAlpha(uint32_t id, float alpha) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.color.a = static_cast<half>(alpha);
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void hideLayer(uint32_t id) {
- setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
- }
-
- void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
-
- void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eColorChanged;
- transactions.back().states.front().state.color.rgb = rgb;
- transactions.back().states.front().layerId = id;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setLayerStack(uint32_t id, int32_t layerStack) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setTouchableRegion(uint32_t id, Region region) {
- 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 = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
- inputInfo->touchableRegion = region;
- inputInfo->token = sp<BBinder>::make();
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
- 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 = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
- if (!inputInfo->token) {
- inputInfo->token = sp<BBinder>::make();
- }
- configureInput(*inputInfo);
-
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
- bool replaceTouchableRegionWithCrop) {
- 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 = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
- inputInfo->touchableRegion = region;
- inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
- transactions.back().states.front().touchCropId = touchCropId;
-
- inputInfo->token = sp<BBinder>::make();
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.frameRateSelectionPriority = priority;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
- int8_t changeFrameRateStrategy) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.frameRate = frameRate;
- transactions.back().states.front().state.frameRateCompatibility = compatibility;
- transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.frameRateCategory = frameRateCategory;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what =
- layer_state_t::eFrameRateSelectionStrategyChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.frameRateSelectionStrategy = strategy;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what =
- layer_state_t::eDefaultFrameRateCompatibilityChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.defaultFrameRateCompatibility =
- defaultFrameRateCompatibility;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setRoundedCorners(uint32_t id, float radius) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.cornerRadius = radius;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eBufferChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().externalTexture = texture;
- transactions.back().states.front().state.bufferData =
- std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(),
- texture->getHeight(), texture->getPixelFormat(),
- texture->getUsage());
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setBuffer(uint32_t id) {
- static uint64_t sBufferId = 1;
- setBuffer(id,
- std::make_shared<renderengine::mock::
- FakeExternalTexture>(1U /*width*/, 1U /*height*/,
- sBufferId++,
- HAL_PIXEL_FORMAT_RGBA_8888,
- GRALLOC_USAGE_PROTECTED /*usage*/));
- }
-
- void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.bufferCrop = bufferCrop;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setDamageRegion(uint32_t id, const Region& damageRegion) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setDataspace(uint32_t id, ui::Dataspace dataspace) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.dataspace = dataspace;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
- layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
-
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.matrix = matrix;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setShadowRadius(uint32_t id, float shadowRadius) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.shadowRadius = shadowRadius;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.trustedOverlay = trustedOverlay;
- mLifecycleManager.applyTransactions(transactions);
- }
-
- void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
-
- transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged;
- transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.dropInputMode = dropInputMode;
- mLifecycleManager.applyTransactions(transactions);
- }
-
LayerLifecycleManager mLifecycleManager;
};
@@ -523,21 +100,6 @@
protected:
LayerSnapshotTestBase() : LayerHierarchyTestBase() {}
- void createRootLayer(uint32_t id) override {
- LayerHierarchyTestBase::createRootLayer(id);
- setColor(id);
- }
-
- void createLayer(uint32_t id, uint32_t parentId) override {
- LayerHierarchyTestBase::createLayer(id, parentId);
- setColor(parentId);
- }
-
- void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
- LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
- setColor(id);
- }
-
void update(LayerSnapshotBuilder& snapshotBuilder) {
mHierarchyBuilder.update(mLifecycleManager);
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a61fa1e..de37b63 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -38,6 +38,7 @@
namespace android::scheduler {
using android::mock::createDisplayMode;
+using android::mock::createVrrDisplayMode;
using namespace com::android::graphics::surfaceflinger;
class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
@@ -53,6 +54,8 @@
static constexpr Fps HI_FPS = 90_Hz;
static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
+ static constexpr auto kVrrModeId = DisplayModeId(2);
+
LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
mFlinger.resetScheduler(mScheduler);
mLifecycleManager = {};
@@ -71,6 +74,13 @@
updateLayerSnapshotsAndLayerHistory(time);
}
+ void setFrontBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) {
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setFrontBuffer(sequence);
+ layer->setDesiredPresentTime(time, false /*autotimestamp*/);
+ updateLayerSnapshotsAndLayerHistory(time);
+ }
+
LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
@@ -135,6 +145,21 @@
return layer;
}
+ auto createLegacyAndFrontedEndLayerWithUid(uint32_t sequence, gui::Uid uid) {
+ std::string layerName = "test layer:" + std::to_string(sequence);
+ auto args = LayerCreationArgs{mFlinger.flinger(),
+ nullptr,
+ layerName,
+ 0,
+ {},
+ std::make_optional<uint32_t>(sequence)};
+ args.ownerUid = uid.val();
+ const auto layer = sp<Layer>::make(args);
+ mFlinger.injectLegacyLayer(layer);
+ createRootLayerWithUid(sequence, uid);
+ return layer;
+ }
+
auto destroyLayer(sp<Layer>& layer) {
uint32_t sequence = static_cast<uint32_t>(layer->sequence);
mFlinger.releaseLegacyLayer(sequence);
@@ -157,12 +182,13 @@
ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
}
- std::shared_ptr<RefreshRateSelector> mSelector =
- std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
- LO_FPS),
- createDisplayMode(DisplayModeId(1),
- HI_FPS)),
- DisplayModeId(0));
+ std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>(
+ makeModes(createDisplayMode(DisplayModeId(0), LO_FPS),
+ createDisplayMode(DisplayModeId(1), HI_FPS),
+ createVrrDisplayMode(kVrrModeId, HI_FPS,
+ hal::VrrConfig{.minFrameIntervalNs =
+ HI_FPS.getPeriodNsecs()})),
+ DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
TestableSurfaceFlinger mFlinger;
@@ -214,6 +240,146 @@
EXPECT_EQ(1u, activeLayerCount());
}
+TEST_F(LayerHistoryIntegrationTest, oneLayer) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ // Max returned if active layers have insufficient history.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ time += LO_FPS_PERIOD;
+ }
+
+ // Max is returned since we have enough history but there is no timestamp votes.
+ for (size_t i = 0; i < 10; i++) {
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ time += LO_FPS_PERIOD;
+ }
+}
+
+TEST_F(LayerHistoryIntegrationTest, gameFrameRateOverrideMapping) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
+
+ auto overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(120_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGameFrameRateOverride) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ const uid_t uid = 0;
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const Fps gameModeFrameRate = Fps::fromValue(60.0f);
+
+ auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+ showLayer(1);
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ // update game default frame rate override
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ LayerHistory::Summary summary;
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += gameDefaultFrameRate.getPeriodNsecs();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
+
+ // test against setFrameRate vote
+ setFrameRate(1,
+ Layer::FrameRate(Fps::fromValue(120.0f), Layer::FrameRateCompatibility::Default));
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ const Fps setFrameRate = Fps::fromValue(120.0f);
+ layerProps.setFrameRateVote =
+ Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default);
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += setFrameRate.getPeriodNsecs();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
+
+ // update game mode frame rate override
+ history().updateGameModeFrameRateOverride(
+ FrameRateOverride({uid, gameModeFrameRate.getValue()}));
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += gameModeFrameRate.getPeriodNsecs();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
+}
+
TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) {
createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -238,6 +404,110 @@
EXPECT_EQ(0u, activeLayerCount());
}
+TEST_F(LayerHistoryIntegrationTest, explicitTimestamp) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVote) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerMinVote) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerMaxVote) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) {
createLegacyAndFrontedEndLayer(1);
setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
@@ -273,6 +543,335 @@
EXPECT_EQ(1, frequentLayerCount(time));
}
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote2) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_vrr) {
+ // Set the test to be on a vrr mode.
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, 0);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Test for MRR device with VRR features enabled.
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_nonVrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+ // The vrr_config flag is explicitly not set false because this test for an MRR device
+ // should still work in a VRR-capable world.
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, 0);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ // Set default to Min so it is obvious that the vote reset triggered.
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
+// the category is NoPreference.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became infrequent
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There are 2 LayerRequirement's due to the frame rate category.
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the layer's category specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // Second LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ hideLayer(1);
+ setFrameRate(1, (12.34_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
+ // votes to consider for refresh rate selection.
+ ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+ SET_FLAG_FOR_TEST(flags::misc1, false);
+
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBuffer(1);
+ setBuffer(2);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(2u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayerDoesNotVote) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBuffer(1);
+ setBuffer(2);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(2u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, frontBufferedLayerVotesMax) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setFrontBuffer(1);
+ showLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setFrontBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // Layer still active due to front buffering, but it's infrequent.
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
@@ -293,6 +892,49 @@
EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
}
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithFixedSourceAndNoPreferenceCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, (45.6_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There are 2 LayerRequirement's due to the frame rate category.
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the layer's category specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // Second LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[1].vote);
+ EXPECT_EQ(45.6_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
TEST_F(LayerHistoryIntegrationTest, multipleLayers) {
auto layer1 = createLegacyAndFrontedEndLayer(1);
auto layer2 = createLegacyAndFrontedEndLayer(2);
@@ -805,7 +1447,15 @@
// layer is active but infrequent.
for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- auto props = layer->getLayerProps();
+ scheduler::LayerProps props = {
+ .visible = false,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
if (i % 3 == 0) {
props.isSmallDirty = false;
} else {
@@ -838,8 +1488,15 @@
// uiLayer is updating small dirty.
for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- auto props = uiLayer->getLayerProps();
- props.isSmallDirty = true;
+ scheduler::LayerProps props = {
+ .visible = false,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = true,
+ .isFrontBuffered = false,
+ };
setBuffer(1);
uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/);
updateLayerSnapshotsAndLayerHistory(time);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
deleted file mode 100644
index 088d0d2..0000000
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ /dev/null
@@ -1,1573 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryTest"
-
-#include <Layer.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#include <common/test/FlagUtils.h>
-#include "FpsOps.h"
-#include "Scheduler/LayerHistory.h"
-#include "Scheduler/LayerInfo.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/MockLayer.h"
-#include "mock/MockSchedulerCallback.h"
-
-using testing::_;
-using testing::Return;
-using testing::ReturnRef;
-
-namespace android::scheduler {
-
-using MockLayer = android::mock::MockLayer;
-
-using android::mock::createDisplayMode;
-using android::mock::createVrrDisplayMode;
-
-// WARNING: LEGACY TESTS FOR LEGACY FRONT END
-// Update LayerHistoryIntegrationTest instead
-class LayerHistoryTest : public testing::Test {
-protected:
- static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs;
- static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize;
- static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
-
- static constexpr Fps LO_FPS = 30_Hz;
- static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
-
- static constexpr Fps HI_FPS = 90_Hz;
- static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
-
- LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
-
- LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
- const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
-
- LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
- // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
- // however, for testing only, a stable order is required, therefore we sort the list here.
- // Any tests requiring ordered results must create layers with names.
- auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
- std::sort(summary.begin(), summary.end(),
- [](const RefreshRateSelector::LayerRequirement& lhs,
- const RefreshRateSelector::LayerRequirement& rhs) -> bool {
- return lhs.name < rhs.name;
- });
- return summary;
- }
-
- size_t layerCount() const { return mScheduler->layerHistorySize(); }
- size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
- return history().mActiveLayerInfos.size();
- }
-
- auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mActiveLayerInfos;
- return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
- return pair.second.second->isFrequent(now).isFrequent;
- });
- }
-
- auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mActiveLayerInfos;
- return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
- return pair.second.second->isAnimating(now);
- });
- }
-
- auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mActiveLayerInfos;
- return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
- return pair.second.second->isFrequent(now).clearHistory;
- });
- }
-
- void setDefaultLayerVote(Layer* layer,
- LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
- auto [found, layerPair] = history().findLayer(layer->getSequence());
- if (found != LayerHistory::LayerStatus::NotFound) {
- layerPair->second->setDefaultLayerVote(vote);
- }
- }
-
- auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); }
- auto createLayer(std::string name) {
- return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
- }
- auto createLayer(std::string name, uint32_t uid) {
- return sp<MockLayer>::make(mFlinger.flinger(), std::move(name), std::move(uid));
- }
-
- void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
- Fps desiredRefreshRate, int numFrames) {
- LayerHistory::Summary summary;
- for (int i = 0; i < numFrames; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += frameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
- }
-
- static constexpr auto kVrrModeId = DisplayModeId(2);
- std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>(
- makeModes(createDisplayMode(DisplayModeId(0), LO_FPS),
- createDisplayMode(DisplayModeId(1), HI_FPS),
- createVrrDisplayMode(kVrrModeId, HI_FPS,
- hal::VrrConfig{.minFrameIntervalNs =
- HI_FPS.getPeriodNsecs()})),
- DisplayModeId(0));
-
- mock::SchedulerCallback mSchedulerCallback;
- TestableSurfaceFlinger mFlinger;
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback);
-};
-
-namespace {
-
-using namespace com::android::graphics::surfaceflinger;
-
-TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(FrameRateCompatibility::NoVote));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- // No layers returned if no layers are active.
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer->getSequence(),
-
- layer->getDefaultFrameRateCompatibility(),
- true /* contentDetectionEnabled */);
-
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(1, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(FrameRateCompatibility::Min));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer->getSequence(),
- layer->getDefaultFrameRateCompatibility(),
- true /* contentDetectionEnabled */);
-
- auto summary = summarizeLayerHistory(time);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
-
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, oneLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- // history().registerLayer(layer, LayerHistory::LayerVoteType::Max);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- // No layers returned if no layers are active.
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- // Max returned if active layers have insufficient history.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- time += LO_FPS_PERIOD;
- }
-
- // Max is returned since we have enough history but there is no timestamp votes.
- for (int i = 0; i < 10; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- time += LO_FPS_PERIOD;
- }
-}
-
-TEST_F(LayerHistoryTest, gameFrameRateOverrideMapping) {
- SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
-
- history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
-
- auto overridePair = history().getGameFrameRateOverride(0);
- EXPECT_EQ(0_Hz, overridePair.first);
- EXPECT_EQ(60_Hz, overridePair.second);
-
- history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
- history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
-
- overridePair = history().getGameFrameRateOverride(0);
- EXPECT_EQ(40_Hz, overridePair.first);
- EXPECT_EQ(60_Hz, overridePair.second);
-
- overridePair = history().getGameFrameRateOverride(1);
- EXPECT_EQ(120_Hz, overridePair.first);
- EXPECT_EQ(0_Hz, overridePair.second);
-
- history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
- history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
-
- overridePair = history().getGameFrameRateOverride(0);
- EXPECT_EQ(40_Hz, overridePair.first);
- EXPECT_EQ(0_Hz, overridePair.second);
-
- overridePair = history().getGameFrameRateOverride(1);
- EXPECT_EQ(0_Hz, overridePair.first);
- EXPECT_EQ(0_Hz, overridePair.second);
-}
-
-TEST_F(LayerHistoryTest, oneLayerGameFrameRateOverride) {
- SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
-
- const uid_t uid = 0;
- const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
- const Fps gameModeFrameRate = Fps::fromValue(60.0f);
- const auto layer = createLayer("GameFrameRateLayer", uid);
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- // update game default frame rate override
- history().updateGameDefaultFrameRateOverride(
- FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
-
- nsecs_t time = systemTime();
- LayerHistory::Summary summary;
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += gameDefaultFrameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
-
- // test against setFrameRate vote
- const Fps setFrameRate = Fps::fromValue(120.0f);
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default)));
-
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += setFrameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
-
- // update game mode frame rate override
- history().updateGameModeFrameRateOverride(
- FrameRateOverride({uid, gameModeFrameRate.getValue()}));
-
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += gameModeFrameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
-}
-
-TEST_F(LayerHistoryTest, oneInvisibleLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- auto summary = summarizeLayerHistory(time);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- // Layer is still considered inactive so we expect to get Min
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- summary = summarizeLayerHistory(time);
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, explicitTimestamp) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerNoVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerMinVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerMaxVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitGte_vrr) {
- // Set the test to be on a vrr mode.
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- mSelector->setActiveMode(kVrrModeId, HI_FPS);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
- Seamlessness::OnlySeamless,
- FrameRateCategory::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-
- // layer became inactive, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-// Test for MRR device with VRR features enabled.
-TEST_F(LayerHistoryTest, oneLayerExplicitGte_nonVrr) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
- // The vrr_config flag is explicitly not set false because this test for an MRR device
- // should still work in a VRR-capable world.
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
- Seamlessness::OnlySeamless,
- FrameRateCategory::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- // Set default to Min so it is obvious that the vote reset triggered.
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitCategory) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- // First LayerRequirement is the frame rate specification
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
-// the category is NoPreference.
-TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless,
- FrameRateCategory::NoPreference)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- EXPECT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became infrequent
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- EXPECT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- // There are 2 LayerRequirement's due to the frame rate category.
- ASSERT_EQ(2, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- // First LayerRequirement is the layer's category specification
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-
- // Second LayerRequirement is the frame rate specification
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(2, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
- SET_FLAG_FOR_TEST(flags::misc1, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(12.34_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
- // votes to consider for refresh rate selection.
- ASSERT_EQ(0, summarizeLayerHistory(time).size());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, multipleLayers) {
- auto layer1 = createLayer("A");
- auto layer2 = createLayer("B");
- auto layer3 = createLayer("C");
-
- EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(3, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- LayerHistory::Summary summary;
-
- // layer1 is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // layer2 is frequent and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- // layer1 is still active but infrequent.
- history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
-
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer1 is no longer active.
- // layer2 is frequent and has low refresh rate.
- for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer2 still has low refresh rate.
- // layer3 has high refresh rate but not enough history.
- constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- if (i % RATIO == 0) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- }
-
- history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer3 becomes recently active.
- history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- summary = summarizeLayerHistory(time);
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer1 expires.
- layer1.clear();
- summary = summarizeLayerHistory(time);
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
- EXPECT_EQ(2, layerCount());
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer2 still has low refresh rate.
- // layer3 becomes inactive.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer2 expires.
- layer2.clear();
- summary = summarizeLayerHistory(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // layer3 becomes active and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer3 expires.
- layer3.clear();
- summary = summarizeLayerHistory(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(0, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, inactiveLayers) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- // the very first updates makes the layer frequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // advance the time for the previous frame to be inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-
- // Now even if we post a quick few frame we should stay infrequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- }
-
- // More quick frames will get us to frequent again
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
- SET_FLAG_FOR_TEST(flags::misc1, false);
-
- auto explicitVisiblelayer = createLayer();
- auto explicitInvisiblelayer = createLayer();
-
- EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- nsecs_t time = systemTime();
-
- // Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
-
- EXPECT_EQ(2, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, invisibleExplicitLayerDoesNotVote) {
- SET_FLAG_FOR_TEST(flags::misc1, true);
-
- auto explicitVisiblelayer = createLayer();
- auto explicitInvisiblelayer = createLayer();
-
- EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- nsecs_t time = systemTime();
-
- // Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
-
- EXPECT_EQ(2, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // another update with the same cadence keep in infrequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // an update as animation will immediately vote for Max
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::AnimationTX);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(1, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) {
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // Layer still active due to front buffering, but it's infrequent.
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // Fill up the window with frequent updates
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (60_Hz).getPeriodNsecs();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // posting a buffer after long inactivity should retain the layer as active
- time += std::chrono::nanoseconds(3s).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting more infrequent buffer should make the layer infrequent
- time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting another buffer should keep the layer infrequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting more buffers would mean starting of an animation, so making the layer frequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(1, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting a buffer after long inactivity should retain the layer as active
- time += std::chrono::nanoseconds(3s).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting another buffer should keep the layer frequent
- time += (60_Hz).getPeriodNsecs();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, inconclusiveLayerBecomingFrequent) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // Fill up the window with frequent updates
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (60_Hz).getPeriodNsecs();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // posting infrequent buffers after long inactivity should make the layer
- // inconclusive but frequent.
- time += std::chrono::nanoseconds(3s).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting more buffers should make the layer frequent and switch the refresh rate to max
- // by clearing the history
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(1, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, getFramerate) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
- EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
-}
-
-TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
- for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
- recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
- }
-}
-
-TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
- recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-
- recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
- SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
-
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
- SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
-
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, smallDirtyLayer) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- LayerHistory::Summary summary;
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- auto props = layer->getLayerProps();
- if (i % 3 == 0) {
- props.isSmallDirty = false;
- } else {
- props.isSmallDirty = true;
- }
-
- history().record(layer->getSequence(), props, time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
-}
-
-TEST_F(LayerHistoryTest, smallDirtyInMultiLayer) {
- auto layer1 = createLayer("UI");
- auto layer2 = createLayer("Video");
-
- EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(30_Hz, Layer::FrameRateCompatibility::Default)));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(2, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- LayerHistory::Summary summary;
-
- // layer1 is updating small dirty.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- auto props = layer1->getLayerProps();
- props.isSmallDirty = true;
- history().record(layer1->getSequence(), props, 0 /*presentTime*/, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
-}
-
-class LayerHistoryTestParameterized : public LayerHistoryTest,
- public testing::WithParamInterface<std::chrono::nanoseconds> {
-};
-
-TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) {
- std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
- auto heuristicLayer = createLayer("HeuristicLayer");
-
- EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- auto infrequentLayer = createLayer("InfrequentLayer");
- EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- const nsecs_t startTime = systemTime();
-
- const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
- startTime, LayerHistory::LayerUpdateType::Buffer);
- history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
- startTime, LayerHistory::LayerUpdateType::Buffer);
-
- nsecs_t time = startTime;
- nsecs_t lastInfrequentUpdate = startTime;
- const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
- int infrequentLayerUpdates = 0;
- while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
- time += heuristicUpdateDelta.count();
- history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
- ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
- totalInfrequentLayerUpdates);
- lastInfrequentUpdate = time;
- history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time,
- time, LayerHistory::LayerUpdateType::Buffer);
- infrequentLayerUpdates++;
- }
-
- if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
- ASSERT_NE(0, summarizeLayerHistory(time).size());
- ASSERT_GE(2, summarizeLayerHistory(time).size());
-
- bool max = false;
- bool min = false;
- Fps heuristic;
- for (const auto& layer : summarizeLayerHistory(time)) {
- if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
- heuristic = layer.desiredRefreshRate;
- } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
- max = true;
- } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
- min = true;
- }
- }
-
- if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
- EXPECT_EQ(24_Hz, heuristic);
- EXPECT_FALSE(max);
- if (summarizeLayerHistory(time).size() == 2) {
- EXPECT_TRUE(min);
- }
- }
- }
- }
-}
-
-INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized,
- ::testing::Values(1s, 2s, 3s, 4s, 5s));
-
-} // namespace
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index bc15dec..c7cc21c 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -61,18 +61,6 @@
class LayerLifecycleManagerTest : public LayerHierarchyTestBase {
protected:
- std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
- return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
- /*parent=*/UNASSIGNED_LAYER_ID,
- /*mirror=*/UNASSIGNED_LAYER_ID));
- }
-
- std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
- return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
- parentId,
- /*mirror=*/UNASSIGNED_LAYER_ID));
- }
-
RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
uint32_t layerId) {
return lifecycleManager.getLayerFromId(layerId);
@@ -631,4 +619,14 @@
}
}
+TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) {
+ // By default the layer has no buffer, so it doesn't need an input info
+ EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+
+ setBuffer(111);
+ mLifecycleManager.commitChanges();
+
+ EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 8b9ac93..75d2fa3 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -27,6 +27,7 @@
#include "LayerHierarchyTest.h"
#include "ui/GraphicTypes.h"
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
@@ -56,6 +57,17 @@
class LayerSnapshotTest : public LayerSnapshotTestBase {
protected:
+ const Layer::FrameRate FRAME_RATE_VOTE1 =
+ Layer::FrameRate(67_Hz, scheduler::FrameRateCompatibility::Default);
+ const Layer::FrameRate FRAME_RATE_VOTE2 =
+ Layer::FrameRate(14_Hz, scheduler::FrameRateCompatibility::Default);
+ const Layer::FrameRate FRAME_RATE_VOTE3 =
+ Layer::FrameRate(99_Hz, scheduler::FrameRateCompatibility::Default);
+ const Layer::FrameRate FRAME_RATE_TREE =
+ Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::NoVote);
+ const Layer::FrameRate FRAME_RATE_NO_VOTE =
+ Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::Default);
+
LayerSnapshotTest() : LayerSnapshotTestBase() {
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
}
@@ -281,21 +293,132 @@
EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged);
}
-TEST_F(LayerSnapshotTest, GameMode) {
+TEST_F(LayerSnapshotTest, ChildrenInheritGameMode) {
+ setGameMode(1, gui::GameMode::Performance);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
+ EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance);
+ EXPECT_EQ(getSnapshot(11)->gameMode, gui::GameMode::Performance);
+}
+
+TEST_F(LayerSnapshotTest, ChildrenCanOverrideGameMode) {
+ setGameMode(1, gui::GameMode::Performance);
+ setGameMode(11, gui::GameMode::Battery);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
+ EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance);
+ EXPECT_EQ(getSnapshot(11)->gameMode, gui::GameMode::Battery);
+}
+
+TEST_F(LayerSnapshotTest, ReparentingUpdatesGameMode) {
+ setGameMode(1, gui::GameMode::Performance);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
+ EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance);
+ EXPECT_EQ(getSnapshot(2)->gameMode, gui::GameMode::Unsupported);
+
+ reparentLayer(2, 1);
+ setZ(2, 2);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(2)->gameMode, gui::GameMode::Performance);
+}
+
+TEST_F(LayerSnapshotTest, UpdateMetadata) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+ // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly,
+ // and not using stale data.
transactions.back().states.front().state.metadata = LayerMetadata();
- transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789);
+
transactions.back().states.front().layerId = 1;
transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+
mLifecycleManager.applyTransactions(transactions);
- EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::GameMode);
- UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Metadata);
+
+ // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = true,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ update(mSnapshotBuilder, args);
+
EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
- EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
- EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789);
+}
+
+TEST_F(LayerSnapshotTest, UpdateMetadataOfHiddenLayers) {
+ hideLayer(1);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+ // This test focuses on metadata used by ARC++ to ensure LayerMetadata is updated correctly,
+ // and not using stale data.
+ transactions.back().states.front().state.metadata = LayerMetadata();
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_UID, 123);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_WINDOW_TYPE, 234);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_TASK_ID, 345);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_MOUSE_CURSOR, 456);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_ACCESSIBILITY_ID, 567);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_OWNER_PID, 678);
+ transactions.back().states.front().state.metadata.setInt32(METADATA_CALLING_UID, 789);
+
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::Metadata | RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::AffectsChildren);
+
+ // Setting includeMetadata=true to ensure metadata update is applied to LayerSnapshot
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = true,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ update(mSnapshotBuilder, args);
+
+ EXPECT_EQ(static_cast<int64_t>(getSnapshot(1)->clientChanges),
+ layer_state_t::eMetadataChanged | layer_state_t::eFlagsChanged);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_UID, -1), 123);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_WINDOW_TYPE, -1), 234);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_TASK_ID, -1), 345);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_MOUSE_CURSOR, -1), 456);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_ACCESSIBILITY_ID, -1), 567);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_OWNER_PID, -1), 678);
+ EXPECT_EQ(getSnapshot(1)->layerMetadata.getInt32(METADATA_CALLING_UID, -1), 789);
}
TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) {
@@ -676,6 +799,155 @@
scheduler::FrameRateCompatibility::Default);
}
+TEST_F(LayerSnapshotTest, frameRateSetAndGet) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent is gets no vote
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetParent) {
+ setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetParentAllVote) {
+ setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0);
+ setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0);
+ setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE2);
+
+ setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE3);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChild) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAllVote) {
+ setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0);
+ setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0);
+ setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAddAfterVote) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ reparentLayer(111, 2);
+ std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+
+ reparentLayer(111, 11);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildRemoveAfterVote) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ reparentLayer(111, 2);
+ std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateAddChildForParentWithTreeVote) {
+ setFrameRate(11, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE);
+
+ setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
TEST_F(LayerSnapshotTest, translateDataspace) {
setDataspace(1, ui::Dataspace::UNKNOWN);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -854,7 +1126,7 @@
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::FrameRateCompatibility::NoVote);
- EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 12 and all descendants (121, 122, 1221) get the requested vote
EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
@@ -945,7 +1217,7 @@
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::FrameRateCompatibility::NoVote);
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default);
- EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 12 and all descendants (121, 122, 1221) get the requested vote
EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
@@ -1304,6 +1576,17 @@
EXPECT_TRUE(foundInputLayer);
}
+TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) {
+ std::vector<uint32_t> visitedUniqueSequences;
+ mSnapshotBuilder.forEachSnapshot(
+ [&](const std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ visitedUniqueSequences.push_back(snapshot->uniqueSequence);
+ },
+ [](const frontend::LayerSnapshot& snapshot) { return snapshot.uniqueSequence == 111; });
+ EXPECT_EQ(visitedUniqueSequences.size(), 1u);
+ EXPECT_EQ(visitedUniqueSequences[0], 111u);
+}
+
TEST_F(LayerSnapshotTest, canOccludePresentation) {
setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation);
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
@@ -1435,4 +1718,221 @@
gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
}
+static constexpr const FloatRect LARGE_FLOAT_RECT{std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max()};
+TEST_F(LayerSnapshotTest, layerVisibleByDefault) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, hideLayerWithZeroMatrix) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ setMatrix(1, 0.f, 0.f, 0.f, 0.f);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, hideLayerWithInfMatrix) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ setMatrix(1, std::numeric_limits<float>::infinity(), 0.f, 0.f,
+ std::numeric_limits<float>::infinity());
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, hideLayerWithNanMatrix) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ setMatrix(1, std::numeric_limits<float>::quiet_NaN(), 0.f, 0.f,
+ std::numeric_limits<float>::quiet_NaN());
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) {
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ setCrop(1, Rect(0, 0, 20, 20));
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */,
+ 20 /* height */,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ setEdgeExtensionEffect(12, LEFT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(LEFT));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(LEFT));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(LEFT));
+
+ setEdgeExtensionEffect(12, RIGHT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(RIGHT));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(RIGHT));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(RIGHT));
+
+ setEdgeExtensionEffect(12, TOP);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(TOP));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(TOP));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(TOP));
+
+ setEdgeExtensionEffect(12, BOTTOM);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(BOTTOM));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(BOTTOM));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(BOTTOM));
+}
+
+TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The left bound is extended when shifting to the right
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ setCrop(1, Rect(0, 0, 20, 20));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = 5.0;
+ setPosition(12, translation, 0);
+ setEdgeExtensionEffect(12, LEFT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0);
+}
+
+TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The right bound is extended when shifting to the left
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ const int crop = 20;
+ setCrop(1, Rect(0, 0, crop, crop));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = -5.0;
+ setPosition(12, translation, 0);
+ setEdgeExtensionEffect(12, RIGHT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.left, 0);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop);
+}
+
+TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The top bound is extended when shifting to the bottom
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ setCrop(1, Rect(0, 0, 20, 20));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = 5.0;
+ setPosition(12, 0, translation);
+ setEdgeExtensionEffect(12, TOP);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0.0);
+}
+
+TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The bottom bound is extended when shifting to the top
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ const int crop = 20;
+ setCrop(1, Rect(0, 0, crop, crop));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = -5.0;
+ setPosition(12, 0, translation);
+ setEdgeExtensionEffect(12, BOTTOM);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.top, 0);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize - translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop);
+}
+
+TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The left bound is extended when shifting to the right
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ const int crop = 20;
+ setCrop(1, Rect(0, 0, crop, crop));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = 5.0;
+ setPosition(12, translation, translation);
+ setEdgeExtensionEffect(12, LEFT | RIGHT | TOP | BOTTOM);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0);
+}
+
+TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) {
+ // By default the layer has no buffer, so we don't expect it to have an input info
+ EXPECT_FALSE(getSnapshot(111)->hasInputInfo());
+
+ setBuffer(111);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot(111)->hasInputInfo());
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL));
+ EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
deleted file mode 100644
index 95e54f6..0000000
--- a/services/surfaceflinger/tests/unittests/LayerTest.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gtest/gtest.h>
-#include <ui/FloatRect.h>
-#include <ui/Transform.h>
-#include <limits>
-
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-
-namespace android {
-namespace {
-
-class LayerTest : public BaseLayerTest {
-protected:
- static constexpr const float MIN_FLOAT = std::numeric_limits<float>::min();
- static constexpr const float MAX_FLOAT = std::numeric_limits<float>::max();
- static constexpr const FloatRect LARGE_FLOAT_RECT{MIN_FLOAT, MIN_FLOAT, MAX_FLOAT, MAX_FLOAT};
-};
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, LayerTest,
- testing::Values(std::make_shared<BufferStateLayerFactory>(),
- std::make_shared<EffectLayerFactory>()),
- PrintToStringParamName);
-
-TEST_P(LayerTest, layerVisibleByDefault) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
- ASSERT_FALSE(layer->isHiddenByPolicy());
-}
-
-TEST_P(LayerTest, hideLayerWithZeroMatrix) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
-
- layer_state_t::matrix22_t matrix{0, 0, 0, 0};
- layer->setMatrix(matrix);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
-
- ASSERT_TRUE(layer->isHiddenByPolicy());
-}
-
-TEST_P(LayerTest, hideLayerWithInfMatrix) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
-
- constexpr const float INF = std::numeric_limits<float>::infinity();
- layer_state_t::matrix22_t matrix{INF, 0, 0, INF};
- layer->setMatrix(matrix);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
-
- ASSERT_TRUE(layer->isHiddenByPolicy());
-}
-
-TEST_P(LayerTest, hideLayerWithNanMatrix) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
-
- constexpr const float QUIET_NAN = std::numeric_limits<float>::quiet_NaN();
- layer_state_t::matrix22_t matrix{QUIET_NAN, 0, 0, QUIET_NAN};
- layer->setMatrix(matrix);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
-
- ASSERT_TRUE(layer->isHiddenByPolicy());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index e74f643..c879280 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -706,7 +706,7 @@
testGpuScenario(config, res);
EXPECT_EQ(res.gpuDurationNanos, 0L);
EXPECT_EQ(res.cpuDurationNanos, 0L);
- EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin()));
+ EXPECT_GE(res.durationNanos, toNanos(29ms + getErrorMargin()));
EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin()));
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index d64cf2f..adbd868 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1566,7 +1566,7 @@
// When frame rates get an equal score, the lower is chosen, unless there are Max votes.
{0_Hz, FrameRateCategory::High, 90_Hz},
{0_Hz, FrameRateCategory::Normal, 60_Hz},
- {0_Hz, FrameRateCategory::Low, 30_Hz},
+ {0_Hz, FrameRateCategory::Low, 60_Hz},
{0_Hz, FrameRateCategory::NoPreference, 60_Hz},
// Cases that have both desired frame rate and frame rate category requirements.
@@ -1613,6 +1613,77 @@
}
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_120_vrr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ // Device with VRR config mode
+ auto selector = createSelector(kVrrMode_120, kModeId120);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ // Prepare a table with the vote and the expected refresh rate
+ const std::initializer_list<Case> testCases = {
+ // Cases that only have frame rate category requirements, but no desired frame rate.
+ // When frame rates get an equal score, the lower is chosen, unless there are Max votes.
+ {0_Hz, FrameRateCategory::High, 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 60_Hz},
+ {0_Hz, FrameRateCategory::Low, 48_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 120_Hz},
+
+ // Cases that have both desired frame rate and frame rate category requirements.
+ {24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal, 60_Hz},
+ {24_Hz, FrameRateCategory::Low, 48_Hz},
+ {30_Hz, FrameRateCategory::NoPreference, 30_Hz},
+
+ // Cases that only have desired frame rate.
+ {30_Hz, FrameRateCategory::Default, 30_Hz},
+ };
+
+ for (auto testCase : testCases) {
+ std::vector<LayerRequirement> layers;
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers).fps)
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ }
+}
+
TEST_P(RefreshRateSelectorTest,
getBestFrameRateMode_withFrameRateCategoryMultiLayers_30_60_90_120) {
auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
@@ -2181,14 +2252,14 @@
// These layers may switch modes because smoothSwitchOnly=false.
{FrameRateCategory::Default, false, 120_Hz, kModeId120},
{FrameRateCategory::NoPreference, false, 120_Hz, kModeId120},
- {FrameRateCategory::Low, false, 30_Hz, kModeId60},
+ {FrameRateCategory::Low, false, 60_Hz, kModeId60},
{FrameRateCategory::Normal, false, 60_Hz, kModeId60},
{FrameRateCategory::High, false, 120_Hz, kModeId120},
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 120_Hz, kModeId120},
{FrameRateCategory::Normal, true, 120_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
@@ -2248,13 +2319,13 @@
{FrameRateCategory::Default, false, 120_Hz},
// TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
{FrameRateCategory::NoPreference, false, 120_Hz},
- {FrameRateCategory::Low, false, 30_Hz},
+ {FrameRateCategory::Low, false, 48_Hz},
{FrameRateCategory::Normal, false, 60_Hz},
{FrameRateCategory::High, false, 120_Hz},
{FrameRateCategory::Default, true, 120_Hz},
// TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
{FrameRateCategory::NoPreference, true, 120_Hz},
- {FrameRateCategory::Low, true, 30_Hz},
+ {FrameRateCategory::Low, true, 48_Hz},
{FrameRateCategory::Normal, true, 60_Hz},
{FrameRateCategory::High, true, 120_Hz},
};
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index fc54a8b..ac09cbc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -34,6 +34,7 @@
#include "mock/MockSchedulerCallback.h"
#include <FrontEnd/LayerHierarchy.h>
+#include <scheduler/FrameTime.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include "FpsOps.h"
@@ -77,6 +78,8 @@
SchedulerTest();
+ static constexpr RefreshRateSelector::LayerRequirement kLayer = {.weight = 1.f};
+
static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
@@ -84,6 +87,9 @@
ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
+ static inline FrameRateMode kDisplay1Mode60_60{60_Hz, kDisplay1Mode60};
+ static inline FrameRateMode kDisplay1Mode120_120{120_Hz, kDisplay1Mode120};
+
static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
@@ -118,10 +124,11 @@
// createConnection call to scheduler makes a createEventConnection call to EventThread. Make
// sure that call gets executed and returns an EventThread::Connection object.
- EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ EXPECT_CALL(*mEventThread, createEventConnection(_))
.WillRepeatedly(Return(mEventThreadConnection));
mScheduler->setEventThread(Cycle::Render, std::move(eventThread));
+ mScheduler->setEventThread(Cycle::LastComposite, std::make_unique<MockEventThread>());
mFlinger.resetScheduler(mScheduler);
}
@@ -161,7 +168,17 @@
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+
+ mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
@@ -187,16 +204,53 @@
kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+ mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
-TEST_F(SchedulerTest, dispatchCachedReportedMode) {
- mScheduler->clearCachedReportedMode();
+TEST_F(SchedulerTest, emitModeChangeEvent) {
+ const auto selectorPtr =
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId());
+ mScheduler->registerDisplay(kDisplayId1, selectorPtr);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+ mScheduler->setContentRequirements({kLayer});
+
+ // No event is emitted in response to idle.
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
- EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+
+ using TimerState = TestableScheduler::TimerState;
+
+ mScheduler->idleTimerCallback(TimerState::Expired);
+ selectorPtr->setActiveMode(kDisplay1Mode60->getId(), 60_Hz);
+
+ auto layer = kLayer;
+ layer.vote = RefreshRateSelector::LayerVoteType::ExplicitExact;
+ layer.desiredRefreshRate = 60_Hz;
+ mScheduler->setContentRequirements({layer});
+
+ // An event is emitted implicitly despite choosing the same mode as when idle.
+ EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode60_60)).Times(1);
+
+ mScheduler->idleTimerCallback(TimerState::Reset);
+
+ mScheduler->setContentRequirements({kLayer});
+
+ // An event is emitted explicitly for the mode change.
+ EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1);
+
+ mScheduler->touchTimerCallback(TimerState::Reset);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
@@ -224,9 +278,16 @@
kDisplay1Mode60->getId()));
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
- EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
-
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(),
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 0, 0},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+ mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, systemTime(),
LayerHistory::LayerUpdateType::Buffer);
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -245,14 +306,12 @@
/*updateAttachedChoreographer*/ false);
}
-TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
+TEST_F(SchedulerTest, chooseDisplayModes) {
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
kDisplay1Mode60->getId()));
- std::vector<RefreshRateSelector::LayerRequirement> layers =
- std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
- mScheduler->setContentRequirements(layers);
+ mScheduler->setContentRequirements({kLayer, kLayer});
GlobalSignals globalSignals = {.idle = true};
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -287,15 +346,14 @@
EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
}
-TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) {
+TEST_F(SchedulerTest, chooseDisplayModesHighHintTouchSignal) {
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
kDisplay1Mode60->getId()));
using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
- std::vector<RefreshRateSelector::LayerRequirement> layers =
- std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+ std::vector<RefreshRateSelector::LayerRequirement> layers = {kLayer, kLayer};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
@@ -370,9 +428,7 @@
kDisplay2Mode60},
GlobalSignals{});
- std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
- {.weight = 1.f}};
- mScheduler->setContentRequirements(layers);
+ mScheduler->setContentRequirements({kLayer, kLayer});
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
@@ -607,7 +663,8 @@
TimePoint::fromNs(2000)));
// Not crossing the min frame period
- vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+ vrrTracker->onFrameBegin(TimePoint::fromNs(2000),
+ {TimePoint::fromNs(1500), TimePoint::fromNs(1500)});
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
TimePoint::fromNs(2500)));
@@ -740,7 +797,7 @@
const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread);
const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread);
- EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ EXPECT_CALL(*mEventThread, createEventConnection(_))
.WillOnce(Return(mockConnection1))
.WillOnce(Return(mockConnection2));
@@ -827,7 +884,6 @@
mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
layer.clear();
- mFlinger.mutableLayersPendingRemoval().clear();
EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
}
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
deleted file mode 100644
index 9899d42..0000000
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/FrameRateUtils.h>
-#include <gui/LayerMetadata.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include "Layer.h"
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-#include "FpsOps.h"
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockVsyncController.h"
-
-namespace android {
-
-using testing::DoAll;
-using testing::Mock;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-using scheduler::LayerHistory;
-
-using FrameRate = Layer::FrameRate;
-using FrameRateCompatibility = Layer::FrameRateCompatibility;
-
-/**
- * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
- */
-class SetFrameRateTest : public BaseLayerTest {
-protected:
- const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
- const FrameRate FRAME_RATE_VOTE3 = FrameRate(99_Hz, FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(), FrameRateCompatibility::Default);
-
- SetFrameRateTest();
-
- void addChild(sp<Layer> layer, sp<Layer> child);
- void removeChild(sp<Layer> layer, sp<Layer> child);
- void commitTransaction();
-
- std::vector<sp<Layer>> mLayers;
-};
-
-SetFrameRateTest::SetFrameRateTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
- layer->addChild(child);
-}
-
-void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
- layer->removeChild(child);
-}
-
-void SetFrameRateTest::commitTransaction() {
- for (auto layer : mLayers) {
- layer->commitTransaction();
- }
-}
-
-namespace {
-
-TEST_P(SetFrameRateTest, SetAndGet) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- layer->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParent) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- child1->setFrameRate(FRAME_RATE_VOTE2.vote);
- parent->setFrameRate(FRAME_RATE_VOTE3.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child2->getFrameRateForLayerTree());
-
- child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE3, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE3, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChild) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- child1->setFrameRate(FRAME_RATE_VOTE2.vote);
- parent->setFrameRate(FRAME_RATE_VOTE3.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- addChild(child1, child2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- removeChild(child1, child2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
- addChild(child1, child2_1);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
-}
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
- testing::Values(std::make_shared<BufferStateLayerFactory>(),
- std::make_shared<EffectLayerFactory>()),
- PrintToStringParamName);
-
-TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(parent, child);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
-
- auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
- history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
- LayerHistory::LayerUpdateType::Buffer);
- history.record(child->getSequence(), child->getLayerProps(), 0, 0,
- LayerHistory::LayerUpdateType::Buffer);
-
- const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
- const auto summary = history.summarize(*selectorPtr, 0);
-
- ASSERT_EQ(2u, summary.size());
- EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[0].desiredRefreshRate);
- EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[1].desiredRefreshRate);
-}
-
-TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- const auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- const auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- const auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- const auto childOfChild1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, childOfChild1);
-
- childOfChild1->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- addChild(parent, child2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, childOfChild1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
index ff7612e..d638024 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
@@ -28,7 +28,6 @@
class ColorMatrixTest : public CommitAndCompositeTest {};
TEST_F(ColorMatrixTest, colorMatrixChanged) {
- mFlinger.enableLayerLifecycleManager();
EXPECT_COLOR_MATRIX_CHANGED(true, true);
mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
@@ -46,7 +45,6 @@
}
TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) {
- mFlinger.enableLayerLifecycleManager();
EXPECT_COLOR_MATRIX_CHANGED(true, true);
mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index e5f2a91..2d3ebb4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -97,7 +97,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
@@ -129,7 +129,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) {
@@ -159,7 +159,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index f8ad8e1..df8f68f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -38,7 +38,7 @@
// Call Expectations
// Destroying the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
// --------------------------------------------------------------------
// Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 0c3e875..8699621 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -42,6 +42,47 @@
using android::hardware::graphics::composer::V2_4::Error;
using android::hardware::graphics::composer::V2_4::VsyncPeriodChangeTimeline;
+MATCHER_P2(ModeSettledTo, dmc, modeId, "") {
+ const auto displayId = arg->getPhysicalId();
+
+ if (const auto desiredOpt = dmc->getDesiredMode(displayId)) {
+ *result_listener << "Unsettled desired mode "
+ << ftl::to_underlying(desiredOpt->mode.modePtr->getId());
+ return false;
+ }
+
+ if (dmc->getActiveMode(displayId).modePtr->getId() != modeId) {
+ *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId);
+ return false;
+ }
+
+ return true;
+}
+
+MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
+ const auto displayId = arg->getPhysicalId();
+ auto& dmc = flinger->mutableDisplayModeController();
+
+ if (!dmc.getDesiredMode(displayId)) {
+ *result_listener << "No desired mode";
+ return false;
+ }
+
+ if (dmc.getDesiredMode(displayId)->mode.modePtr->getId() != modeId) {
+ *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId);
+ return false;
+ }
+
+ // VsyncModulator should react to mode switches on the pacesetter display.
+ if (displayId == flinger->scheduler()->pacesetterDisplayId() &&
+ !flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
+ *result_listener << "VsyncModulator did not shift to early phase";
+ return false;
+ }
+
+ return true;
+}
+
class DisplayModeSwitchingTest : public DisplayTransactionTest {
public:
void SetUp() override {
@@ -58,8 +99,7 @@
setupScheduler(selectorPtr);
- mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- DisplayHotplugEvent::CONNECTED);
+ mFlinger.onComposerHalHotplugEvent(kInnerDisplayHwcId, DisplayHotplugEvent::CONNECTED);
mFlinger.configureAndCommit();
auto vsyncController = std::make_unique<mock::VsyncController>();
@@ -87,8 +127,13 @@
static constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
static constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+ static constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
auto injectOuterDisplay() {
- constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+ // 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)));
constexpr bool kIsPrimary = false;
TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
@@ -142,16 +187,6 @@
mAppEventThread = eventThread.get();
auto sfEventThread = std::make_unique<mock::EventThread>();
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid)));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid)));
-
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
@@ -169,129 +204,139 @@
TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp);
}
-TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequired) {
- ftl::FakeGuard guard(kMainThreadContext);
+TEST_F(DisplayModeSwitchingTest, changeRefreshRateWithRefreshRequired) {
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz)));
- mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
-
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, false, 0, 120));
-
- ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90);
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90);
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
mFlinger.commit();
-
Mock::VerifyAndClearExpectations(mComposer);
- EXPECT_TRUE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
EXPECT_CALL(*mAppEventThread,
onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
+
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90);
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90));
}
-TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshRequired) {
- ftl::FakeGuard guard(kMainThreadContext);
+TEST_F(DisplayModeSwitchingTest, changeRefreshRateWithoutRefreshRequired) {
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ constexpr bool kAllowGroupSwitching = true;
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(
+ mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz, kAllowGroupSwitching)));
- mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
-
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, true, 0, 120));
-
- ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90);
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
- EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90);
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
EXPECT_CALL(*mAppEventThread,
onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90);
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90));
}
-TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
- ftl::FakeGuard guard(kMainThreadContext);
+TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnTwoDisplaysWithoutRefreshRequired) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
- // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
- // is still being processed the later call will be respected.
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz,
+ true)));
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60, 60_Hz,
+ true)));
- mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
-
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, false, 0, 120));
-
- const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90);
-
- mFlinger.commit();
-
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId120, false, 0, 180));
-
- ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120);
-
- EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120);
-
- mFlinger.commit();
-
- ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120);
-
- mFlinger.commit();
-
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId120);
-}
-
-TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRequired) {
- ftl::FakeGuard guard(kMainThreadContext);
-
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
-
- mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
-
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120));
-
- ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90_4K);
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
- EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K);
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
+ EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60);
+
+ EXPECT_CALL(*mAppEventThread, onModeChanged(_)).Times(2);
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
+}
+
+TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+ // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
+ // is still being processed the later call will be respected.
+
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz)));
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
+
+ mFlinger.commit();
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId120,
+ 180_Hz)));
+
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId120));
+
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId120);
+
+ mFlinger.commit();
+
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId120));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId120));
+}
+
+TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) {
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90_4K,
+ 120_Hz)));
+
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90_4K));
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ // and complete the mode change.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90_4K);
EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplayId, true));
@@ -310,61 +355,12 @@
mFlinger.commit();
- EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
- EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90_4K);
-}
-
-MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
- const auto displayId = arg->getPhysicalId();
- auto& dmc = flinger->mutableDisplayModeController();
-
- if (!dmc.getDesiredMode(displayId)) {
- *result_listener << "No desired mode";
- return false;
- }
-
- if (dmc.getDesiredMode(displayId)->mode.modePtr->getId() != modeId) {
- *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId);
- return false;
- }
-
- // VsyncModulator should react to mode switches on the pacesetter display.
- if (displayId == flinger->scheduler()->pacesetterDisplayId() &&
- !flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
- *result_listener << "VsyncModulator did not shift to early phase";
- return false;
- }
-
- return true;
-}
-
-MATCHER_P2(ModeSettledTo, dmc, modeId, "") {
- const auto displayId = arg->getPhysicalId();
-
- if (const auto desiredOpt = dmc->getDesiredMode(displayId)) {
- *result_listener << "Unsettled desired mode "
- << ftl::to_underlying(desiredOpt->mode.modePtr->getId());
- return false;
- }
-
- ftl::FakeGuard guard(kMainThreadContext);
-
- if (dmc->getActiveMode(displayId).modePtr->getId() != modeId) {
- *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId);
- return false;
- }
-
- return true;
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90_4K));
}
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());
@@ -381,13 +377,11 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz)));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, 120_Hz)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
@@ -414,8 +408,7 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, 120_Hz)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60);
@@ -434,10 +427,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());
@@ -454,13 +443,11 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz)));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, 120_Hz)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
@@ -486,8 +473,7 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz)));
EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
@@ -511,11 +497,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());
@@ -532,13 +513,11 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, 120_Hz)));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, 120_Hz)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
@@ -566,8 +545,8 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId120, false,
- 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId120,
+ 120_Hz)));
EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId120);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index b620830..9bf344c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -265,6 +265,13 @@
processesHotplugConnectCommon<SimpleExternalDisplayCase>();
}
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectNonSecureExternalDisplay) {
+ // Inject a primary display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+
+ processesHotplugConnectCommon<SimpleExternalDisplayNonSecureCase>();
+}
+
TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
// Inject both a primary and external display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -273,13 +280,29 @@
// TODO: This is an unnecessary call.
EXPECT_CALL(*mComposer,
getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
- SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay<kSecure>::PORT),
+ SetArgPointee<2>(TertiaryDisplay<kSecure>::GET_IDENTIFICATION_DATA()),
Return(Error::NONE)));
ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
}
+TEST_F(DisplayTransactionCommitTest,
+ ignoresHotplugConnectNonSecureIfPrimaryAndExternalAlreadyConnected) {
+ // Inject both a primary and external display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+ ExternalDisplayVariant::injectHwcDisplay(this);
+
+ // TODO: This is an unnecessary call.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay<kSecure>::PORT),
+ SetArgPointee<2>(TertiaryDisplay<kSecure>::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+
+ ignoresHotplugConnectCommon<SimpleTertiaryDisplayNonSecureCase>();
+}
+
TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) {
EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(),
testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
@@ -289,6 +312,10 @@
processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
}
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectNonSecureExternalDisplay) {
+ processesHotplugDisconnectCommon<SimpleExternalDisplayNonSecureCase>();
+}
+
TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) {
EXPECT_EXIT(
[this] {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
index db6df22..4bc134f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 897f9a0..aef467a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -48,7 +48,7 @@
TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1);
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
constexpr HWDisplayId displayId1 = 456;
mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED);
@@ -73,7 +73,7 @@
.WillOnce(Return(Error::NONE));
// A single commit should be scheduled for both configure calls.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
mFlinger.configure();
@@ -116,7 +116,7 @@
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
mFlinger.configure();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index eaf4684..5231965 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -28,7 +28,7 @@
TEST_F(InitializeDisplaysTest, initializesDisplays) {
// Scheduled by the display transaction, and by powering on each display.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(3);
EXPECT_CALL(static_cast<mock::VSyncTracker&>(
mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 83e2f98..fed7b2e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -271,7 +271,7 @@
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 933d03d..352000e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -239,7 +239,7 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId);
+ mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt);
DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
.setResolution(Case::Display::RESOLUTION)
.setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
deleted file mode 100644
index 0e5f1ea..0000000
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "TestableSurfaceFlinger.h"
-
-namespace android {
-
-using testing::_;
-using testing::Return;
-
-class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
-public:
- SurfaceFlingerUpdateLayerMetadataSnapshotTest() { mFlinger.setupMockScheduler(); }
-
-protected:
- sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) {
- LayerCreationArgs args =
- LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata};
- inOutlayerMetadata = args.metadata;
- return sp<Layer>::make(args);
- }
-
- TestableSurfaceFlinger mFlinger;
-};
-
-class LayerMetadataBuilder {
-public:
- LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {}
-
- LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) {
- mLayerMetadata.setInt32(key, value);
- return *this;
- }
-
- LayerMetadata build() { return mLayerMetadata; }
-
-private:
- LayerMetadata mLayerMetadata;
-};
-
-bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) {
- return lhs.mMap == rhs.mMap;
-}
-
-std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) {
- stream << "LayerMetadata{";
- for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) {
- if (it != layerMetadata.mMap.cbegin()) {
- stream << ", ";
- }
- stream << layerMetadata.itemToString(it->first, ":");
- }
- return stream << "}";
-}
-
-// Test that the snapshot's layer metadata is set.
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) {
- auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
- auto layer = createLayer("layer", layerMetadata);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
-}
-
-// Test that snapshot layer metadata is set by merging the child's metadata on top of its
-// parent's metadata.
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) {
- auto layerAMetadata = LayerMetadataBuilder()
- .setInt32(METADATA_OWNER_UID, 1)
- .setInt32(METADATA_TASK_ID, 2)
- .build();
- auto layerA = createLayer("parent", layerAMetadata);
- auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build();
- auto layerB = createLayer("child", layerBMetadata);
- layerA->addChild(layerB);
- layerA->commitChildList();
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
- auto expectedChildMetadata =
- LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
- EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
-}
-
-// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
-// that parent's relative layer metadata.
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) {
- auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
- auto layerA = createLayer("relative-parent", layerAMetadata);
- auto layerAHandle = layerA->getHandle();
- auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build();
- auto layerB = createLayer("relative-child", layerBMetadata);
- layerB->setRelativeLayer(layerAHandle, 1);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
-}
-
-// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
-// two other layers.
-//
-// Layer
-// A
-// / \
-// B D
-// /
-// C
-//
-// Z-order Relatives
-// B <- D <- C
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) {
- auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
- auto layerA = createLayer("layer-a", layerAMetadata);
- auto layerBMetadata = LayerMetadataBuilder()
- .setInt32(METADATA_TASK_ID, 2)
- .setInt32(METADATA_OWNER_PID, 3)
- .build();
- auto layerB = createLayer("layer-b", layerBMetadata);
- auto layerBHandle = layerB->getHandle();
- LayerMetadata layerCMetadata;
- auto layerC = createLayer("layer-c", layerCMetadata);
- auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
- auto layerD = createLayer("layer-d", layerDMetadata);
- auto layerDHandle = layerD->getHandle();
- layerB->addChild(layerC);
- layerA->addChild(layerB);
- layerA->addChild(layerD);
- layerC->setRelativeLayer(layerDHandle, 1);
- layerD->setRelativeLayer(layerBHandle, 1);
- layerA->commitChildList();
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- auto expectedLayerDRelativeMetadata =
- LayerMetadataBuilder()
- // From layer A, parent of relative parent
- .setInt32(METADATA_OWNER_UID, 1)
- // From layer B, relative parent
- .setInt32(METADATA_TASK_ID, 2)
- .setInt32(METADATA_OWNER_PID, 3)
- // added by layer creation args
- .setInt32(gui::METADATA_CALLING_UID,
- layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
- .build();
- EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
- auto expectedLayerCRelativeMetadata =
- LayerMetadataBuilder()
- // From layer A, parent of relative parent
- .setInt32(METADATA_OWNER_UID, 1)
- // From layer B, relative parent of relative parent
- .setInt32(METADATA_OWNER_PID, 3)
- // From layer D, relative parent
- .setInt32(METADATA_TASK_ID, 4)
- // added by layer creation args
- .setInt32(gui::METADATA_CALLING_UID,
- layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
- .build();
- EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
-}
-
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
- updatesRelativeMetadataMultipleRelativeChildren) {
- auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
- auto layerA = createLayer("layer-a", layerAMetadata);
- auto layerAHandle = layerA->getHandle();
- LayerMetadata layerBMetadata;
- auto layerB = createLayer("layer-b", layerBMetadata);
- LayerMetadata layerCMetadata;
- auto layerC = createLayer("layer-c", layerCMetadata);
- layerB->setRelativeLayer(layerAHandle, 1);
- layerC->setRelativeLayer(layerAHandle, 2);
- layerA->commitChildList();
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerC);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
- EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1f7bf5f..9de3346 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -62,7 +62,7 @@
}
MOCK_METHOD(void, scheduleConfigure, (), (override));
- MOCK_METHOD(void, scheduleFrame, (), (override));
+ MOCK_METHOD(void, scheduleFrame, (Duration), (override));
MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) {
@@ -74,10 +74,8 @@
void setEventThread(Cycle cycle, std::unique_ptr<EventThread> eventThreadPtr) {
if (cycle == Cycle::Render) {
mRenderEventThread = std::move(eventThreadPtr);
- mRenderEventConnection = mRenderEventThread->createEventConnection();
} else {
mLastCompositeEventThread = std::move(eventThreadPtr);
- mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection();
}
}
@@ -178,6 +176,11 @@
mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
}
+ using Scheduler::TimerState;
+
+ using Scheduler::idleTimerCallback;
+ using Scheduler::touchTimerCallback;
+
void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) {
std::lock_guard<std::mutex> lock(mPolicyLock);
mPolicy.contentRequirements = std::move(layers);
@@ -190,15 +193,7 @@
return Scheduler::chooseDisplayModes();
}
- void dispatchCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
- Scheduler::dispatchCachedReportedMode();
- }
-
- void clearCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
- mPolicy.cachedModeChangedParams.reset();
- }
+ using Scheduler::onDisplayModeChanged;
void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) {
auto schedule = getVsyncSchedule(id);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 0d13dc5..c043b88 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -16,7 +16,6 @@
#pragma once
-#include <algorithm>
#include <chrono>
#include <memory>
#include <variant>
@@ -44,7 +43,6 @@
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "RenderArea.h"
-#include "Scheduler/MessageQueue.h"
#include "Scheduler/RefreshRateSelector.h"
#include "SurfaceFlinger.h"
#include "TestableScheduler.h"
@@ -60,7 +58,6 @@
#include "Scheduler/VSyncTracker.h"
#include "Scheduler/VsyncController.h"
-#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
@@ -88,9 +85,7 @@
public:
~Factory() = default;
- std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
- return nullptr;
- }
+ std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps /*currentRefreshRate*/) override {
@@ -276,17 +271,6 @@
auto eventThread = makeMock<mock::EventThread>(options.useNiceMock);
auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock);
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid)));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid)));
-
auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
@@ -326,35 +310,25 @@
auto& mutableStateLock() { return mFlinger->mStateLock; }
- static auto findOutputLayerForDisplay(const sp<Layer>& layer,
- const sp<const DisplayDevice>& display) {
- return layer->findOutputLayerForDisplay(display.get());
- }
-
- static void setLayerSidebandStream(const sp<Layer>& layer,
- const sp<NativeHandle>& sidebandStream) {
- layer->mDrawingState.sidebandStream = sidebandStream;
- layer->mSidebandStream = sidebandStream;
- layer->editLayerSnapshot()->sidebandStream = sidebandStream;
+ compositionengine::OutputLayer* findOutputLayerForDisplay(
+ uint32_t layerId, const sp<const DisplayDevice>& display) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ if (mFlinger->mLegacyLayers.find(layerId) == mFlinger->mLegacyLayers.end()) {
+ return nullptr;
+ }
+ return mFlinger->mLegacyLayers[layerId]->findOutputLayerForDisplay(display.get());
}
void setLayerCompositionType(const sp<Layer>& layer,
aidl::android::hardware::graphics::composer3::Composition type) {
- auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
+ auto outputLayer = findOutputLayerForDisplay(static_cast<uint32_t>(layer->sequence),
+ mFlinger->getDefaultDisplayDevice());
LOG_ALWAYS_FATAL_IF(!outputLayer);
auto& state = outputLayer->editState();
LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc);
(*state.hwc).hwcCompositionType = type;
}
- static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) {
- layer->mPotentialCursor = potentialCursor;
- }
-
- static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
- layer->mDrawingParent = drawingParent;
- }
-
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -395,7 +369,7 @@
targets.try_emplace(id, &frameTargeter.target());
targeters.try_emplace(id, &frameTargeter);
}
-
+ mFlinger->setTransactionFlags(eTransactionFlushNeeded);
mFlinger->commit(displayId, targets);
if (composite) {
@@ -500,19 +474,19 @@
auto layers = getLayerSnapshotsFn();
auto layerFEs = mFlinger->extractLayerFEs(layers);
- return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling,
+ return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling,
false /* grayscale */, false /* isProtected */,
- captureResults, displayState, layers, layerFEs);
+ false /* attachGainmap */, captureResults, displayState,
+ layers, layerFEs);
}
- auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
- std::unordered_set<uint32_t> excludeLayerIds,
- const LayerVector::Visitor& visitor) {
- return mFlinger->traverseLayersInLayerStack(layerStack, uid, excludeLayerIds, visitor);
+ auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
+ return mFlinger->getLayerSnapshotsForScreenshots(layerStack, uid,
+ std::unordered_set<uint32_t>{});
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
- ui::DisplayPrimaries &primaries) {
+ ui::DisplayPrimaries& primaries) {
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
@@ -530,7 +504,7 @@
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -586,8 +560,6 @@
return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult);
}
- void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
-
void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
ui::DynamicDisplayInfo* dynamicDisplayInfo) {
mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
@@ -622,6 +594,18 @@
mFlinger->mNewLayers.emplace_back(std::move(layer));
}
+ // Used to add a layer before updateLayerSnapshots is called.
+ // Must have transactionsFlushed enabled for the new layer to be updated.
+ void addLayer(uint32_t layerId) {
+ std::scoped_lock<std::mutex> lock(mFlinger->mCreatedLayersLock);
+ LayerCreationArgs args(std::make_optional(layerId));
+ args.flinger = this->mFlinger.get();
+ auto layer = std::make_unique<frontend::RequestedLayerState>(args);
+ auto legacyLayer = sp<Layer>::make(args);
+ injectLegacyLayer(legacyLayer);
+ mFlinger->mNewLayers.emplace_back(std::move(layer));
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -639,12 +623,24 @@
void injectLegacyLayer(sp<Layer> layer) {
FTL_FAKE_GUARD(kMainThreadContext,
mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer);
- };
+ }
void releaseLegacyLayer(uint32_t sequence) {
FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mLegacyLayers.erase(sequence));
+ }
+
+ auto getLegacyLayer(uint32_t layerId) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return mFlinger->mLegacyLayers[layerId];
};
+ void destroyAllLayerHandles() {
+ ftl::FakeGuard guard(kMainThreadContext);
+ for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) {
+ mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId);
+ }
+ }
+
auto setLayerHistoryDisplayArea(uint32_t displayArea) {
return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
};
@@ -707,7 +703,6 @@
}
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
- auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS {
return mFlinger->mLayerSnapshotBuilder;
}
@@ -719,10 +714,6 @@
return mFlinger->initTransactionTraceWriter();
}
- // Needed since mLayerLifecycleManagerEnabled is false by default and must
- // be enabled for tests to go through the new front end path.
- void enableLayerLifecycleManager() { mFlinger->mLayerLifecycleManagerEnabled = true; }
-
void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod,
TimePoint expectedPresentTime, Fps frameInterval,
std::optional<Period> timeoutOpt) {
@@ -781,7 +772,6 @@
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
- mFlinger->mLayersPendingRemoval.clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 7fb9247..fab1f6d 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "TransactionApplicationTest"
+#include <binder/Binder.h>
#include <common/test/FlagUtils.h>
#include <compositionengine/Display.h>
#include <compositionengine/mock/DisplaySurface.h>
@@ -26,10 +27,10 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/fake/BufferData.h>
#include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
#include <ui/MockFence.h>
#include <utils/String8.h>
#include <vector>
-#include <binder/Binder.h>
#include "FrontEnd/TransactionHandler.h"
#include "TestableSurfaceFlinger.h"
@@ -55,6 +56,7 @@
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.setupMockScheduler();
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.flinger()->addTransactionReadyFilters();
}
@@ -65,6 +67,7 @@
}
TestableSurfaceFlinger mFlinger;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
struct TransactionInfo {
Vector<ComposerState> states;
@@ -102,7 +105,7 @@
void NotPlacedOnTransactionQueue(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transaction;
setupSingle(transaction, flags,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -126,7 +129,7 @@
void PlaceOnTransactionQueue(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
@@ -152,7 +155,7 @@
void BlockedByPriorTransaction(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
nsecs_t time = systemTime();
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(2);
// transaction that should go on the pending thread
TransactionInfo transactionA;
@@ -214,7 +217,7 @@
TEST_F(TransactionApplicationTest, AddToPendingQueue) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
@@ -235,7 +238,7 @@
TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
@@ -323,15 +326,17 @@
transaction1.states[0].state.bufferData =
std::make_shared<fake::BufferData>(/* bufferId */ 1, /* width */ 1, /* height */ 1,
/* pixelFormat */ 0, /* outUsage */ 0);
+ mFlinger.addLayer(1);
+ bool out;
+ mFlinger.updateLayerSnapshots(VsyncId{1}, 0, /* transactionsFlushed */ true, out);
transaction1.states[0].externalTexture =
std::make_shared<FakeExternalTexture>(*transaction1.states[0].state.bufferData);
- transaction1.states[0].state.surface =
- sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
- ->getHandle();
+ transaction1.states[0].state.surface = mFlinger.getLegacyLayer(1)->getHandle();
auto fence = sp<mock::MockFence>::make();
EXPECT_CALL(*fence, getStatus()).WillRepeatedly(Return(Fence::Status::Unsignaled));
transaction1.states[0].state.bufferData->acquireFence = std::move(fence);
transaction1.states[0].state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
+ transaction1.states[0].layerId = 1;
transaction1.isAutoTimestamp = true;
// Transaction 2 should be ready to be applied.
@@ -361,8 +366,7 @@
}
mFlinger.getPendingTransactionQueue().clear();
mFlinger.commitTransactionsLocked(eTransactionMask);
- mFlinger.mutableCurrentState().layersSortedByZ.clear();
- mFlinger.mutableDrawingState().layersSortedByZ.clear();
+ mFlinger.destroyAllLayerHandles();
}
static sp<Fence> fence(Fence::Status status) {
@@ -371,8 +375,7 @@
return fence;
}
- ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what,
- std::optional<sp<IBinder>> layerHandle = std::nullopt) {
+ ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
ComposerState state;
state.state.bufferData =
std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
@@ -380,9 +383,6 @@
/* outUsage */ 0);
state.state.bufferData->acquireFence = std::move(fence);
state.state.layerId = layerId;
- state.state.surface = layerHandle.value_or(
- sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
- ->getHandle());
state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
state.state.what = what;
@@ -418,6 +418,19 @@
size_t expectedTransactionsPending) {
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ std::unordered_set<uint32_t> createdLayers;
+ for (auto transaction : transactions) {
+ for (auto& state : transaction.states) {
+ auto layerId = static_cast<uint32_t>(state.state.layerId);
+ if (createdLayers.find(layerId) == createdLayers.end()) {
+ mFlinger.addLayer(layerId);
+ createdLayers.insert(layerId);
+ }
+ }
+ }
+ bool unused;
+ bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
+ /*transactionsFlushed=*/true, unused);
for (auto transaction : transactions) {
std::vector<ResolvedComposerState> resolvedStates;
@@ -427,6 +440,9 @@
resolvedState.state = std::move(state.state);
resolvedState.externalTexture =
std::make_shared<FakeExternalTexture>(*resolvedState.state.bufferData);
+ resolvedState.layerId = static_cast<uint32_t>(state.state.layerId);
+ resolvedState.state.surface =
+ mFlinger.getLegacyLayer(resolvedState.layerId)->getHandle();
resolvedStates.emplace_back(resolvedState);
}
@@ -458,9 +474,8 @@
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleSignaledFromTheQueue) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
+ const auto kLayerId = 10;
const auto kExpectedTransactionsPending = 0u;
-
const auto signaledTransaction =
createTransactionInfo(kApplyToken,
{createComposerState(kLayerId, fence(Fence::Status::Signaled),
@@ -773,7 +788,7 @@
TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
+ const auto kLayerId = 10;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 85b61f8..abfab9a 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -94,7 +94,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- FrameTimelineInfo{});
+ FrameTimelineInfo{}, gui::GameMode::Unsupported);
commitTransaction(layer.get());
nsecs_t latchTime = 25;
@@ -112,7 +112,8 @@
EXPECT_CALL(*mFlinger.getFrameTracer(),
traceFence(layerId, bufferId, frameNumber, presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0));
- layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming);
+ layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming,
+ gui::GameMode::Unsupported);
}
};
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 46733b9..9a68d75 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -72,7 +72,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_TRUE(layer->mDrawingState.bufferSurfaceFrameTX == nullptr);
const auto surfaceFrame = layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
@@ -99,7 +100,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -134,7 +136,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -151,7 +154,8 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -180,7 +184,8 @@
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -197,7 +202,8 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -232,11 +238,13 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
}
@@ -246,7 +254,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferlessSurfaceFrame1 =
@@ -255,7 +264,8 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 4;
ftInfo2.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferlessSurfaceFrame2 = layer->mDrawingState.bufferlessSurfaceFramesTX[4];
@@ -275,7 +285,8 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3,
+ gui::GameMode::Unsupported);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -302,58 +313,6 @@
EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState());
}
- void PendingSurfaceFramesRemovedAfterClassification() {
- sp<Layer> layer = createLayer();
-
- sp<Fence> fence1(sp<Fence>::make());
- auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
- BufferData bufferData;
- bufferData.acquireFence = fence1;
- bufferData.frameNumber = 1;
- bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
- bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
- std::shared_ptr<renderengine::ExternalTexture> externalTexture1 = std::make_shared<
- renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
- 1ULL /* bufferId */,
- HAL_PIXEL_FORMAT_RGBA_8888,
- 0ULL /*usage*/);
- FrameTimelineInfo ftInfo;
- ftInfo.vsyncId = 1;
- ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
- ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
- const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
-
- sp<Fence> fence2(sp<Fence>::make());
- auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
- bufferData.acquireFence = fence2;
- bufferData.frameNumber = 1;
- bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
- bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
- std::shared_ptr<renderengine::ExternalTexture> externalTexture2 = std::make_shared<
- renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
- 1ULL /* bufferId */,
- HAL_PIXEL_FORMAT_RGBA_8888,
- 0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
- acquireFence2->signalForTest(12);
-
- ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
- auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
-
- commitTransaction(layer.get());
- layer->updateTexImage(15);
-
- // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
- // pendingJankClassifications.
- EXPECT_EQ(2u, layer->mPendingJankClassifications.size());
- presentedSurfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz,
- /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
- layer->releasePendingBuffer(25);
-
- EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
- }
-
void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
sp<Layer> layer = createLayer();
@@ -372,7 +331,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -392,7 +352,8 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv,
+ gui::GameMode::Unsupported);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -413,7 +374,8 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2,
+ gui::GameMode::Unsupported);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -445,8 +407,7 @@
void MultipleCommitsBeforeLatch() {
sp<Layer> layer = createLayer();
- uint32_t surfaceFramesPendingClassification = 0;
- std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
+ std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> surfaceFrames;
for (int i = 0; i < 10; i += 2) {
sp<Fence> fence(sp<Fence>::make());
BufferData bufferData;
@@ -462,58 +423,52 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10,
+ gui::GameMode::Unsupported);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
- auto& bufferlessSurfaceFrame =
- layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2);
- bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame);
+
+ surfaceFrames.push_back(layer->mDrawingState.bufferSurfaceFrameTX);
+ surfaceFrames.push_back(
+ layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2));
commitTransaction(layer.get());
- surfaceFramesPendingClassification += 2;
- EXPECT_EQ(surfaceFramesPendingClassification,
- layer->mPendingJankClassifications.size());
}
auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
layer->updateTexImage(15);
// BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
// Since we don't have access to DisplayFrame here, trigger an onPresent directly.
- for (auto& surfaceFrame : bufferlessSurfaceFrames) {
- surfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz,
- /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+ // The odd indices are the bufferless frames.
+ for (uint32_t i = 1; i < 10; i += 2) {
+ surfaceFrames[i]->onPresent(20, JankType::None, 90_Hz, 90_Hz,
+ /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
}
presentedBufferSurfaceFrame->onPresent(20, JankType::None, 90_Hz, 90_Hz,
/*displayDeadlineDelta*/ 0,
/*displayPresentDelta*/ 0);
- // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame
- ASSERT_EQ(10u, surfaceFramesPendingClassification);
- ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size());
-
// For the frames upto 8, the bufferSurfaceFrame should have been dropped while the
// bufferlessSurfaceFrame presented
for (uint32_t i = 0; i < 8; i += 2) {
- auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i];
- auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1];
+ auto bufferSurfaceFrame = surfaceFrames[i];
+ auto bufferlessSurfaceFrame = surfaceFrames[i + 1];
EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped);
EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
}
{
- auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u];
- auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u];
+ auto bufferSurfaceFrame = surfaceFrames[8];
+ auto bufferlessSurfaceFrame = surfaceFrames[9];
EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented);
EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
}
layer->releasePendingBuffer(25);
-
- // There shouldn't be any pending classifications. Everything should have been cleared.
- EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
}
};
@@ -541,10 +496,6 @@
MultipleSurfaceFramesPresentedTogether();
}
-TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) {
- PendingSurfaceFramesRemovedAfterClassification();
-}
-
TEST_F(TransactionSurfaceFrameTest,
BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer) {
BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 7bf1674..f8f08c7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -20,6 +20,7 @@
#include <gui/SurfaceComposerClient.h>
#include <cstdint>
#include "Client.h"
+#include "Layer.h"
#include <layerproto/LayerProtoHeader.h>
#include "FrontEnd/LayerCreationArgs.h"
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index 1cf14ae..e27af0e 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -127,13 +127,11 @@
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- layer->setSidebandStream(stream, FrameTimelineInfo{}, 20);
- mFlinger.mutableCurrentState().layersSortedByZ.add(layer);
+ layer->setSidebandStream(stream, FrameTimelineInfo{}, 20, gui::GameMode::Unsupported);
mTunnelModeEnabledReporter->updateTunnelModeStatus();
mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
- mFlinger.mutableCurrentState().layersSortedByZ.remove(layer);
layer = nullptr;
mTunnelModeEnabledReporter->updateTunnelModeStatus();
@@ -151,14 +149,12 @@
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20);
+ layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20,
+ gui::GameMode::Unsupported);
- mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer);
- mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream);
mTunnelModeEnabledReporter->updateTunnelModeStatus();
EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
- mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream);
layerWithSidebandStream = nullptr;
mTunnelModeEnabledReporter->updateTunnelModeStatus();
EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 3b09554..b63f299 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -19,6 +19,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <scheduler/FrameTime.h>
#include <scheduler/Timer.h>
#include "Scheduler/VSyncDispatchTimerQueue.h"
@@ -51,7 +52,7 @@
bool isVSyncInPhase(nsecs_t, Fps) final { return false; }
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
void setRenderRate(Fps, bool) final {}
- void onFrameBegin(TimePoint, TimePoint) final {}
+ void onFrameBegin(TimePoint, scheduler::FrameTime) final {}
void onFrameMissed(TimePoint) final {}
void dump(std::string&) const final {}
bool isCurrentMode(const ftl::NonNull<DisplayModePtr>&) const final { return false; };
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 5109ea6..918107d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -673,6 +673,36 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
+TEST_F(VSyncPredictorTest, setRenderRateWhenRenderRateGoesDown) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ Fps frameRate = Fps::fromPeriodNsecs(1000);
+ vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ frameRate = Fps::fromPeriodNsecs(3000);
+ vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false);
+ EXPECT_TRUE(vrrTracker.isVSyncInPhase(2000, frameRate));
+}
+
TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
@@ -871,18 +901,22 @@
EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
- vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(2000),
+ {TimePoint::fromNs(1500), TimePoint::fromNs(1500)});
EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000));
EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500));
// Miss when starting 4500 and expect the next vsync will be at 5000 (next one)
- vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3500),
+ {TimePoint::fromNs(2500), TimePoint::fromNs(2500)});
vrrTracker.onFrameMissed(TimePoint::fromNs(4500));
EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
- vrrTracker.onFrameBegin(TimePoint::fromNs(7000), TimePoint::fromNs(6500));
- EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(7000),
+ {TimePoint::fromNs(6500), TimePoint::fromNs(6500)});
+ EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 7000));
+ EXPECT_EQ(9500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}
TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) {
@@ -913,7 +947,7 @@
// SF starts to catch up
EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
- vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000), {TimePoint::fromNs(0), TimePoint::fromNs(0)});
// SF misses last frame (3000) and observes that when committing (4000)
EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
@@ -922,17 +956,20 @@
// SF wakes up again instead of the (4000) missed frame
EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
- vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(4500),
+ {TimePoint::fromNs(4500), TimePoint::fromNs(4500)});
// Timeline shifted. The app needs to get the next frame at (7500) as its last frame (6500) will
// be presented at (7500)
EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
- vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(5500),
+ {TimePoint::fromNs(4500), TimePoint::fromNs(4500)});
EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500));
EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500));
- vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(6500),
+ {TimePoint::fromNs(5500), TimePoint::fromNs(5500)});
}
TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
@@ -990,6 +1027,65 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(13000));
}
+TEST_F(VSyncPredictorTest, timelineNotAdjustedForEarlyPresent) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+
+ constexpr auto kLastConfirmedExpectedPresentTime = TimePoint::fromNs(1000);
+ constexpr auto kLastActualSignalTime = TimePoint::fromNs(700); // presented early
+ vrrTracker.onFrameBegin(TimePoint::fromNs(1400),
+ {kLastActualSignalTime, kLastConfirmedExpectedPresentTime});
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000));
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000));
+}
+
+TEST_F(VSyncPredictorTest, adjustsOnlyMinFrameViolatingVrrTimeline) {
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig{.minFrameIntervalNs =
+ static_cast<int32_t>(minFrameRate.getPeriodNsecs())};
+ ftl::NonNull<DisplayModePtr> mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig));
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
+ auto lastConfirmedSignalTime = TimePoint::fromNs(1500);
+ auto lastConfirmedExpectedPresentTime = TimePoint::fromNs(1000);
+ vrrTracker.onFrameBegin(TimePoint::fromNs(2000),
+ {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime});
+ EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 1500));
+
+ minFrameRate = Fps::fromPeriodNsecs(2000);
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ lastConfirmedSignalTime = TimePoint::fromNs(2500);
+ lastConfirmedExpectedPresentTime = TimePoint::fromNs(2500);
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000),
+ {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime});
+ // Enough time without adjusting vsync to present with new rate on time, no need of adjustment
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 3500));
+}
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 184dada..f472d8f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -180,6 +180,12 @@
MOCK_METHOD1(onHotplugDisconnect, void(Display));
MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
+ MOCK_METHOD(
+ Error, getRequestedLuts,
+ (Display,
+ std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*));
+ MOCK_METHOD(Error, setLayerLuts,
+ (Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 602bdfc..5edd2cd 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -35,6 +35,7 @@
(const, override));
MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));
+ MOCK_METHOD(std::optional<ui::Size>, getPhysicalSizeInMm, (), (const override));
MOCK_METHOD(hal::Error, acceptChanges, (), (override));
MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (),
@@ -109,6 +110,9 @@
MOCK_METHOD(hal::Error, getOverlaySupport,
(aidl::android::hardware::graphics::composer3::OverlayProperties *),
(const override));
+ MOCK_METHOD(hal::Error, getRequestedLuts,
+ (std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
+ (override));
};
class Layer : public HWC2::Layer {
@@ -143,6 +147,8 @@
(const std::string &, bool, const std::vector<uint8_t> &), (override));
MOCK_METHOD(hal::Error, setBrightness, (float), (override));
MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
+ MOCK_METHOD(hal::Error, setLuts,
+ (std::vector<aidl::android::hardware::graphics::composer3::Lut>&), (override));
};
} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
index 7b18a82..4d35d4d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
@@ -22,14 +22,13 @@
namespace android::mock {
-inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode,
- bool allowGroupSwitching, float minFps,
- float maxFps) {
+inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode, Fps maxFps,
+ bool allowGroupSwitching = false) {
gui::DisplayModeSpecs specs;
specs.defaultMode = ftl::to_underlying(defaultMode);
specs.allowGroupSwitching = allowGroupSwitching;
- specs.primaryRanges.physical.min = minFps;
- specs.primaryRanges.physical.max = maxFps;
+ specs.primaryRanges.physical.min = 0.f;
+ specs.primaryRanges.physical.max = maxFps.getValue();
specs.primaryRanges.render = specs.primaryRanges.physical;
specs.appRequestRanges = specs.primaryRanges;
return specs;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8dd1a34..7398cbe 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -24,21 +24,11 @@
class EventThread : public android::EventThread {
public:
- static constexpr auto kCallingUid = static_cast<uid_t>(0);
-
EventThread();
~EventThread() override;
- // TODO(b/302035909): workaround otherwise gtest complains about
- // error: no viable conversion from
- // 'tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration> &&>' to 'const
- // tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration>>'
- sp<EventThreadConnection> createEventConnection(EventRegistrationFlags flags) const override {
- return createEventConnection(false, flags);
- }
- MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (bool, EventRegistrationFlags),
- (const));
-
+ MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags),
+ (const, override));
MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 002fa9f..45f86fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -24,28 +24,18 @@
class MockLayer : public Layer {
public:
MockLayer(SurfaceFlinger* flinger, std::string name)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
- EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
- .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
- }
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {
- EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
- .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
- }
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
- MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
- MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
- MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
- MOCK_METHOD(bool, isFrontBuffered, (), (const, override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 0ac4b68..25dd68e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -26,7 +26,6 @@
MOCK_METHOD(void, requestHardwareVsync, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
- MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
MOCK_METHOD(void, onChoreographerAttached, (), (override));
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
@@ -38,7 +37,6 @@
void requestHardwareVsync(PhysicalDisplayId, bool) override {}
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
- void triggerOnFrameRateOverridesChanged() override {}
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
void onCommitNotComposited() override {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 4f44d1b..8d6d1d3 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -18,6 +18,8 @@
#include <gmock/gmock.h>
+#include <scheduler/FrameTime.h>
+
#include "Scheduler/VSyncTracker.h"
namespace android::mock {
@@ -37,7 +39,7 @@
MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override));
MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
MOCK_METHOD(void, setRenderRate, (Fps, bool), (override));
- MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));
+ MOCK_METHOD(void, onFrameBegin, (TimePoint, scheduler::FrameTime), (override));
MOCK_METHOD(void, onFrameMissed, (TimePoint), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));
MOCK_METHOD(bool, isCurrentMode, (const ftl::NonNull<DisplayModePtr>&), (const, override));
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
index 07916b6..253bad7 100644
--- a/services/surfaceflinger/tests/utils/ColorUtils.h
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -74,8 +74,8 @@
static void applyMatrix(half3& color, const mat3& mat) {
half3 ret = half3(0);
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j = 0; j < 3; j++) {
ret[i] = ret[i] + color[j] * mat[j][i];
}
}
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 1675584..0bedcd1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,7 +15,7 @@
*/
#pragma once
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/SyncScreenCaptureListener.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/FenceResult.h>
@@ -39,7 +39,7 @@
const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB);
const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
binder::Status status = sf->captureDisplay(captureArgs, captureListener);
status_t err = statusTFromBinderStatus(status);
@@ -77,7 +77,7 @@
const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB);
const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
binder::Status status = sf->captureLayers(captureArgs, captureListener);
status_t err = statusTFromBinderStatus(status);
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 2002bdf..4735ae5 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -33,19 +33,19 @@
],
aidl: {
- local_include_dirs: ["include"],
- include_dirs: [
- "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
- ],
- export_aidl_headers: true
+ local_include_dirs: ["include"],
+ include_dirs: [
+ "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
+ ],
+ export_aidl_headers: true,
},
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
- "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator-V3-ndk",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index c1795f5..283a5f0 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "VibratorHalController"
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
#include <hardware/vibrator.h>
#include <utils/Log.h>
@@ -27,10 +27,10 @@
#include <vibratorservice/VibratorHalController.h>
#include <vibratorservice/VibratorHalWrapper.h>
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
using std::chrono::milliseconds;
@@ -38,7 +38,7 @@
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = android::hardware::vibrator;
+namespace Aidl = aidl::android::hardware::vibrator;
namespace android {
@@ -53,10 +53,14 @@
return nullptr;
}
- sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>();
- if (aidlHal) {
- ALOGV("Successfully connected to Vibrator HAL AIDL service.");
- return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
+ auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default";
+ if (AServiceManager_isDeclared(serviceName.c_str())) {
+ std::shared_ptr<Aidl::IVibrator> hal = Aidl::IVibrator::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
+ if (hal) {
+ ALOGV("Successfully connected to Vibrator HAL AIDL service.");
+ return std::make_shared<AidlHalWrapper>(std::move(scheduler), std::move(hal));
+ }
}
sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index f10ba44..4ac1618 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -16,8 +16,8 @@
#define LOG_TAG "VibratorHalWrapper"
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
#include <hardware/vibrator.h>
#include <cmath>
@@ -26,12 +26,15 @@
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
-using android::hardware::vibrator::Braking;
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+using aidl::android::hardware::vibrator::PwleV2Primitive;
+using aidl::android::hardware::vibrator::VendorEffect;
using std::chrono::milliseconds;
@@ -39,7 +42,7 @@
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = android::hardware::vibrator;
+namespace Aidl = aidl::android::hardware::vibrator;
namespace android {
@@ -93,9 +96,25 @@
if (mInfoCache.mMaxAmplitudes.isFailed()) {
mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal();
}
+ if (mInfoCache.mMaxEnvelopeEffectSize.isFailed()) {
+ mInfoCache.mMaxEnvelopeEffectSize = getMaxEnvelopeEffectSizeInternal();
+ }
+ if (mInfoCache.mMinEnvelopeEffectControlPointDuration.isFailed()) {
+ mInfoCache.mMinEnvelopeEffectControlPointDuration =
+ getMinEnvelopeEffectControlPointDurationInternal();
+ }
+ if (mInfoCache.mMaxEnvelopeEffectControlPointDuration.isFailed()) {
+ mInfoCache.mMaxEnvelopeEffectControlPointDuration =
+ getMaxEnvelopeEffectControlPointDurationInternal();
+ }
return mInfoCache.get();
}
+HalResult<void> HalWrapper::performVendorEffect(const VendorEffect&, const std::function<void()>&) {
+ ALOGV("Skipped performVendorEffect because it's not available in Vibrator HAL");
+ return HalResult<void>::unsupported();
+}
+
HalResult<milliseconds> HalWrapper::performComposedEffect(const std::vector<CompositeEffect>&,
const std::function<void()>&) {
ALOGV("Skipped performComposedEffect because it's not available in Vibrator HAL");
@@ -108,6 +127,12 @@
return HalResult<void>::unsupported();
}
+HalResult<void> HalWrapper::composePwleV2(const std::vector<PwleV2Primitive>&,
+ const std::function<void()>&) {
+ ALOGV("Skipped composePwleV2 because it's not available in Vibrator HAL");
+ return HalResult<void>::unsupported();
+}
+
HalResult<Capabilities> HalWrapper::getCapabilities() {
std::lock_guard<std::mutex> lock(mInfoMutex);
if (mInfoCache.mCapabilities.isFailed()) {
@@ -196,11 +221,28 @@
ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL");
return HalResult<std::vector<float>>::unsupported();
}
+HalResult<int32_t> HalWrapper::getMaxEnvelopeEffectSizeInternal() {
+ ALOGV("Skipped getMaxEnvelopeEffectSizeInternal because it's not available "
+ "in Vibrator HAL");
+ return HalResult<int32_t>::unsupported();
+}
+
+HalResult<milliseconds> HalWrapper::getMinEnvelopeEffectControlPointDurationInternal() {
+ ALOGV("Skipped getMinEnvelopeEffectControlPointDurationInternal because it's not "
+ "available in Vibrator HAL");
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<milliseconds> HalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() {
+ ALOGV("Skipped getMaxEnvelopeEffectControlPointDurationInternal because it's not "
+ "available in Vibrator HAL");
+ return HalResult<milliseconds>::unsupported();
+}
// -------------------------------------------------------------------------------------------------
HalResult<void> AidlHalWrapper::ping() {
- return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+ return HalResultFactory::fromStatus(AIBinder_ping(getHal()->asBinder().get()));
}
void AidlHalWrapper::tryReconnect() {
@@ -208,7 +250,7 @@
if (!result.isOk()) {
return;
}
- sp<Aidl::IVibrator> newHandle = result.value();
+ std::shared_ptr<Aidl::IVibrator> newHandle = result.value();
if (newHandle) {
std::lock_guard<std::mutex> lock(mHandleMutex);
mHandle = std::move(newHandle);
@@ -220,7 +262,8 @@
HalResult<Capabilities> capabilities = getCapabilities();
bool supportsCallback = capabilities.isOk() &&
static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
- auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+ auto cb = supportsCallback ? ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback)
+ : nullptr;
auto ret = HalResultFactory::fromStatus(getHal()->on(timeout.count(), cb));
if (!supportsCallback && ret.isOk()) {
@@ -255,13 +298,14 @@
HalResult<Capabilities> capabilities = getCapabilities();
bool supportsCallback = capabilities.isOk() &&
static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
- auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+ auto cb = supportsCallback ? ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback)
+ : nullptr;
int32_t lengthMs;
- auto result = getHal()->perform(effect, strength, cb, &lengthMs);
+ auto status = getHal()->perform(effect, strength, cb, &lengthMs);
milliseconds length = milliseconds(lengthMs);
- auto ret = HalResultFactory::fromStatus<milliseconds>(result, length);
+ auto ret = HalResultFactory::fromStatus<milliseconds>(std::move(status), length);
if (!supportsCallback && ret.isOk()) {
mCallbackScheduler->schedule(completionCallback, length);
}
@@ -269,11 +313,18 @@
return ret;
}
+HalResult<void> AidlHalWrapper::performVendorEffect(
+ const VendorEffect& effect, const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
+ return HalResultFactory::fromStatus(getHal()->performVendorEffect(effect, cb));
+}
+
HalResult<milliseconds> AidlHalWrapper::performComposedEffect(
const std::vector<CompositeEffect>& primitives,
const std::function<void()>& completionCallback) {
// This method should always support callbacks, so no need to double check.
- auto cb = new HalCallbackWrapper(completionCallback);
+ auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
auto durations = getPrimitiveDurations().valueOr({});
milliseconds duration(0);
@@ -294,40 +345,47 @@
HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives,
const std::function<void()>& completionCallback) {
// This method should always support callbacks, so no need to double check.
- auto cb = new HalCallbackWrapper(completionCallback);
+ auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb));
}
+HalResult<void> AidlHalWrapper::composePwleV2(const std::vector<PwleV2Primitive>& composite,
+ const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
+ return HalResultFactory::fromStatus(getHal()->composePwleV2(composite, cb));
+}
+
HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
- int32_t capabilities = 0;
- auto result = getHal()->getCapabilities(&capabilities);
- return HalResultFactory::fromStatus<Capabilities>(result,
- static_cast<Capabilities>(capabilities));
+ int32_t cap = 0;
+ auto status = getHal()->getCapabilities(&cap);
+ auto capabilities = static_cast<Capabilities>(cap);
+ return HalResultFactory::fromStatus<Capabilities>(std::move(status), capabilities);
}
HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
std::vector<Effect> supportedEffects;
- auto result = getHal()->getSupportedEffects(&supportedEffects);
- return HalResultFactory::fromStatus<std::vector<Effect>>(result, supportedEffects);
+ auto status = getHal()->getSupportedEffects(&supportedEffects);
+ return HalResultFactory::fromStatus<std::vector<Effect>>(std::move(status), supportedEffects);
}
HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() {
std::vector<Braking> supportedBraking;
- auto result = getHal()->getSupportedBraking(&supportedBraking);
- return HalResultFactory::fromStatus<std::vector<Braking>>(result, supportedBraking);
+ auto status = getHal()->getSupportedBraking(&supportedBraking);
+ return HalResultFactory::fromStatus<std::vector<Braking>>(std::move(status), supportedBraking);
}
HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
std::vector<CompositePrimitive> supportedPrimitives;
- auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
- return HalResultFactory::fromStatus<std::vector<CompositePrimitive>>(result,
+ auto status = getHal()->getSupportedPrimitives(&supportedPrimitives);
+ return HalResultFactory::fromStatus<std::vector<CompositePrimitive>>(std::move(status),
supportedPrimitives);
}
HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal(
const std::vector<CompositePrimitive>& supportedPrimitives) {
std::vector<milliseconds> durations;
- constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+ constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>();
constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
durations.resize(primitiveCount);
@@ -340,8 +398,8 @@
continue;
}
int32_t duration = 0;
- auto result = getHal()->getPrimitiveDuration(primitive, &duration);
- auto halResult = HalResultFactory::fromStatus<int32_t>(result, duration);
+ auto status = getHal()->getPrimitiveDuration(primitive, &duration);
+ auto halResult = HalResultFactory::fromStatus<int32_t>(std::move(status), duration);
if (halResult.isUnsupported()) {
// Should not happen, supported primitives should always support requesting duration.
ALOGE("Supported primitive %zu returned unsupported for getPrimitiveDuration",
@@ -349,7 +407,7 @@
}
if (halResult.isFailed()) {
// Fail entire request if one request has failed.
- return HalResult<std::vector<milliseconds>>::failed(result.toString8().c_str());
+ return HalResult<std::vector<milliseconds>>::failed(halResult.errorMessage());
}
durations[primitiveIdx] = milliseconds(duration);
}
@@ -359,59 +417,77 @@
HalResult<milliseconds> AidlHalWrapper::getPrimitiveDelayMaxInternal() {
int32_t delay = 0;
- auto result = getHal()->getCompositionDelayMax(&delay);
- return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay));
+ auto status = getHal()->getCompositionDelayMax(&delay);
+ return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(delay));
}
HalResult<milliseconds> AidlHalWrapper::getPrimitiveDurationMaxInternal() {
int32_t delay = 0;
- auto result = getHal()->getPwlePrimitiveDurationMax(&delay);
- return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay));
+ auto status = getHal()->getPwlePrimitiveDurationMax(&delay);
+ return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(delay));
}
HalResult<int32_t> AidlHalWrapper::getCompositionSizeMaxInternal() {
int32_t size = 0;
- auto result = getHal()->getCompositionSizeMax(&size);
- return HalResultFactory::fromStatus<int32_t>(result, size);
+ auto status = getHal()->getCompositionSizeMax(&size);
+ return HalResultFactory::fromStatus<int32_t>(std::move(status), size);
}
HalResult<int32_t> AidlHalWrapper::getPwleSizeMaxInternal() {
int32_t size = 0;
- auto result = getHal()->getPwleCompositionSizeMax(&size);
- return HalResultFactory::fromStatus<int32_t>(result, size);
+ auto status = getHal()->getPwleCompositionSizeMax(&size);
+ return HalResultFactory::fromStatus<int32_t>(std::move(status), size);
}
HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
float minFrequency = 0;
- auto result = getHal()->getFrequencyMinimum(&minFrequency);
- return HalResultFactory::fromStatus<float>(result, minFrequency);
+ auto status = getHal()->getFrequencyMinimum(&minFrequency);
+ return HalResultFactory::fromStatus<float>(std::move(status), minFrequency);
}
HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
float f0 = 0;
- auto result = getHal()->getResonantFrequency(&f0);
- return HalResultFactory::fromStatus<float>(result, f0);
+ auto status = getHal()->getResonantFrequency(&f0);
+ return HalResultFactory::fromStatus<float>(std::move(status), f0);
}
HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() {
float frequencyResolution = 0;
- auto result = getHal()->getFrequencyResolution(&frequencyResolution);
- return HalResultFactory::fromStatus<float>(result, frequencyResolution);
+ auto status = getHal()->getFrequencyResolution(&frequencyResolution);
+ return HalResultFactory::fromStatus<float>(std::move(status), frequencyResolution);
}
HalResult<float> AidlHalWrapper::getQFactorInternal() {
float qFactor = 0;
- auto result = getHal()->getQFactor(&qFactor);
- return HalResultFactory::fromStatus<float>(result, qFactor);
+ auto status = getHal()->getQFactor(&qFactor);
+ return HalResultFactory::fromStatus<float>(std::move(status), qFactor);
}
HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() {
std::vector<float> amplitudes;
- auto result = getHal()->getBandwidthAmplitudeMap(&litudes);
- return HalResultFactory::fromStatus<std::vector<float>>(result, amplitudes);
+ auto status = getHal()->getBandwidthAmplitudeMap(&litudes);
+ return HalResultFactory::fromStatus<std::vector<float>>(std::move(status), amplitudes);
}
-sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
+HalResult<int32_t> AidlHalWrapper::getMaxEnvelopeEffectSizeInternal() {
+ int32_t size = 0;
+ auto status = getHal()->getPwleV2CompositionSizeMax(&size);
+ return HalResultFactory::fromStatus<int32_t>(std::move(status), size);
+}
+
+HalResult<milliseconds> AidlHalWrapper::getMinEnvelopeEffectControlPointDurationInternal() {
+ int32_t durationMs = 0;
+ auto status = getHal()->getPwleV2PrimitiveDurationMinMillis(&durationMs);
+ return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
+}
+
+HalResult<milliseconds> AidlHalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() {
+ int32_t durationMs = 0;
+ auto status = getHal()->getPwleV2PrimitiveDurationMaxMillis(&durationMs);
+ return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
+}
+
+std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
}
@@ -420,8 +496,7 @@
template <typename I>
HalResult<void> HidlHalWrapper<I>::ping() {
- auto result = getHal()->ping();
- return HalResultFactory::fromReturn(result);
+ return HalResultFactory::fromReturn(getHal()->ping());
}
template <typename I>
@@ -436,8 +511,8 @@
template <typename I>
HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
const std::function<void()>& completionCallback) {
- auto result = getHal()->on(timeout.count());
- auto ret = HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ auto status = getHal()->on(timeout.count());
+ auto ret = HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
if (ret.isOk()) {
mCallbackScheduler->schedule(completionCallback, timeout);
}
@@ -446,15 +521,15 @@
template <typename I>
HalResult<void> HidlHalWrapper<I>::off() {
- auto result = getHal()->off();
- return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ auto status = getHal()->off();
+ return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
}
template <typename I>
HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
- auto result = getHal()->setAmplitude(amp);
- return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ auto status = getHal()->setAmplitude(amp);
+ return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
}
template <typename I>
@@ -480,7 +555,7 @@
hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
Capabilities capabilities =
result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
- return HalResultFactory::fromReturn<Capabilities>(result, capabilities);
+ return HalResultFactory::fromReturn<Capabilities>(std::move(result), capabilities);
}
template <typename I>
@@ -499,7 +574,7 @@
auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
milliseconds length = milliseconds(lengthMs);
- auto ret = HalResultFactory::fromReturn<milliseconds>(result, status, length);
+ auto ret = HalResultFactory::fromReturn<milliseconds>(std::move(result), status, length);
if (ret.isOk()) {
mCallbackScheduler->schedule(completionCallback, length);
}
@@ -604,7 +679,7 @@
sp<V1_3::IVibrator> hal = getHal();
auto amplitudeResult = hal->supportsAmplitudeControl();
if (!amplitudeResult.isOk()) {
- return HalResultFactory::fromReturn<Capabilities>(amplitudeResult, capabilities);
+ return HalResultFactory::fromReturn<Capabilities>(std::move(amplitudeResult), capabilities);
}
auto externalControlResult = hal->supportsExternalControl();
@@ -619,7 +694,8 @@
}
}
- return HalResultFactory::fromReturn<Capabilities>(externalControlResult, capabilities);
+ return HalResultFactory::fromReturn<Capabilities>(std::move(externalControlResult),
+ capabilities);
}
// -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index aa5b7fc..ba35d15 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -20,7 +20,7 @@
#include <vibratorservice/VibratorManagerHalController.h>
-namespace Aidl = android::hardware::vibrator;
+namespace Aidl = aidl::android::hardware::vibrator;
namespace android {
@@ -29,10 +29,15 @@
std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) {
static bool gHalExists = true;
if (gHalExists) {
- sp<Aidl::IVibratorManager> hal = waitForVintfService<Aidl::IVibratorManager>();
- if (hal) {
- ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
- return std::make_shared<AidlManagerHalWrapper>(std::move(scheduler), hal);
+ auto serviceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
+ if (AServiceManager_isDeclared(serviceName.c_str())) {
+ std::shared_ptr<Aidl::IVibratorManager> hal = Aidl::IVibratorManager::fromBinder(
+ ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
+ if (hal) {
+ ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
+ return std::make_shared<AidlManagerHalWrapper>(std::move(scheduler),
+ std::move(hal));
+ }
}
}
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 1341266..93ec781 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -20,7 +20,7 @@
#include <vibratorservice/VibratorManagerHalWrapper.h>
-namespace Aidl = android::hardware::vibrator;
+namespace Aidl = aidl::android::hardware::vibrator;
namespace android {
@@ -75,10 +75,11 @@
std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
- std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
- sp<Aidl::IVibrator> vibrator;
- auto result = this->getHal()->getVibrator(vibratorId, &vibrator);
- return HalResultFactory::fromStatus<sp<Aidl::IVibrator>>(result, vibrator);
+ std::function<HalResult<std::shared_ptr<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
+ std::shared_ptr<Aidl::IVibrator> vibrator;
+ auto status = this->getHal()->getVibrator(vibratorId, &vibrator);
+ return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrator>>(std::move(status),
+ vibrator);
};
auto result = reconnectFn();
if (!result.isOk()) {
@@ -93,11 +94,13 @@
}
HalResult<void> AidlManagerHalWrapper::ping() {
- return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+ return HalResultFactory::fromStatus(AIBinder_ping(getHal()->asBinder().get()));
}
void AidlManagerHalWrapper::tryReconnect() {
- sp<Aidl::IVibratorManager> newHandle = checkVintfService<Aidl::IVibratorManager>();
+ auto aidlServiceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
+ std::shared_ptr<Aidl::IVibratorManager> newHandle = Aidl::IVibratorManager::fromBinder(
+ ndk::SpAIBinder(AServiceManager_checkService(aidlServiceName.c_str())));
if (newHandle) {
std::lock_guard<std::mutex> lock(mHandleMutex);
mHandle = std::move(newHandle);
@@ -111,9 +114,9 @@
return HalResult<ManagerCapabilities>::ok(*mCapabilities);
}
int32_t cap = 0;
- auto result = getHal()->getCapabilities(&cap);
+ auto status = getHal()->getCapabilities(&cap);
auto capabilities = static_cast<ManagerCapabilities>(cap);
- auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(result, capabilities);
+ auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(std::move(status), capabilities);
if (ret.isOk()) {
// Cache copy of returned value.
mCapabilities.emplace(ret.value());
@@ -128,8 +131,8 @@
return HalResult<std::vector<int32_t>>::ok(*mVibratorIds);
}
std::vector<int32_t> ids;
- auto result = getHal()->getVibratorIds(&ids);
- auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(result, ids);
+ auto status = getHal()->getVibratorIds(&ids);
+ auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(std::move(status), ids);
if (ret.isOk()) {
// Cache copy of returned value and the individual controllers.
mVibratorIds.emplace(ret.value());
@@ -178,7 +181,8 @@
HalResult<ManagerCapabilities> capabilities = getCapabilities();
bool supportsCallback = capabilities.isOk() &&
static_cast<int32_t>(capabilities.value() & ManagerCapabilities::TRIGGER_CALLBACK);
- auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+ auto cb = supportsCallback ? ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback)
+ : nullptr;
return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
}
@@ -196,7 +200,7 @@
return ret;
}
-sp<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
+std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
}
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 5437995..915d6c7 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -28,12 +28,12 @@
"VibratorHalControllerBenchmarks.cpp",
],
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
"libvibratorservice",
- "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator-V3-ndk",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 9b30337..5c7c9f4 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -16,16 +16,15 @@
#define LOG_TAG "VibratorHalControllerBenchmarks"
+#include <android/binder_process.h>
#include <benchmark/benchmark.h>
-#include <binder/ProcessState.h>
#include <vibratorservice/VibratorHalController.h>
#include <future>
-using ::android::enum_range;
-using ::android::hardware::vibrator::CompositeEffect;
-using ::android::hardware::vibrator::CompositePrimitive;
-using ::android::hardware::vibrator::Effect;
-using ::android::hardware::vibrator::EffectStrength;
+using ::aidl::android::hardware::vibrator::CompositeEffect;
+using ::aidl::android::hardware::vibrator::CompositePrimitive;
+using ::aidl::android::hardware::vibrator::Effect;
+using ::aidl::android::hardware::vibrator::EffectStrength;
using ::benchmark::Counter;
using ::benchmark::Fixture;
using ::benchmark::kMicrosecond;
@@ -115,8 +114,8 @@
class VibratorBench : public Fixture {
public:
void SetUp(State& /*state*/) override {
- android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
- android::ProcessState::self()->startThreadPool();
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
mController.init();
}
@@ -388,11 +387,11 @@
return;
}
- for (const auto& effect : enum_range<Effect>()) {
+ for (const auto& effect : ndk::enum_range<Effect>()) {
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
continue;
}
- for (const auto& strength : enum_range<EffectStrength>()) {
+ for (const auto& strength : ndk::enum_range<EffectStrength>()) {
b->Args({static_cast<long>(effect), static_cast<long>(strength)});
}
}
@@ -533,7 +532,7 @@
return;
}
- for (const auto& primitive : enum_range<CompositePrimitive>()) {
+ for (const auto& primitive : ndk::enum_range<CompositePrimitive>()) {
if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
continue;
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index f97442d..a1cb3fa 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -17,8 +17,8 @@
#ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H
#define ANDROID_OS_VIBRATORHALCONTROLLER_H
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android-base/thread_annotations.h>
-#include <android/hardware/vibrator/IVibrator.h>
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 39c4eb4..4938b15 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -17,10 +17,12 @@
#ifndef ANDROID_OS_VIBRATORHALWRAPPER_H
#define ANDROID_OS_VIBRATORHALWRAPPER_H
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
#include <android-base/thread_annotations.h>
+#include <android/binder_manager.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/BnVibratorCallback.h>
-#include <android/hardware/vibrator/IVibrator.h>
#include <binder/IServiceManager.h>
#include <vibratorservice/VibratorCallbackScheduler.h>
@@ -98,43 +100,49 @@
class HalResultFactory {
public:
template <typename T>
- static HalResult<T> fromStatus(binder::Status status, T data) {
- return status.isOk() ? HalResult<T>::ok(data) : fromFailedStatus<T>(status);
+ static HalResult<T> fromStatus(ndk::ScopedAStatus&& status, T data) {
+ return status.isOk() ? HalResult<T>::ok(std::move(data))
+ : fromFailedStatus<T>(std::move(status));
}
template <typename T>
- static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data) {
- return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<T>::ok(data)
- : fromFailedStatus<T>(status);
+ static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status&& status, T data) {
+ return (status == hardware::vibrator::V1_0::Status::OK)
+ ? HalResult<T>::ok(std::move(data))
+ : fromFailedStatus<T>(std::move(status));
}
template <typename T, typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) {
- return ret.isOk() ? HalResult<T>::ok(data) : fromFailedReturn<T, R>(ret);
+ static HalResult<T> fromReturn(hardware::Return<R>&& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(std::move(data))
+ : fromFailedReturn<T, R>(std::move(ret));
}
template <typename T, typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret,
+ static HalResult<T> fromReturn(hardware::Return<R>&& ret,
hardware::vibrator::V1_0::Status status, T data) {
- return ret.isOk() ? fromStatus<T>(status, data) : fromFailedReturn<T, R>(ret);
+ return ret.isOk() ? fromStatus<T>(std::move(status), std::move(data))
+ : fromFailedReturn<T, R>(std::move(ret));
}
static HalResult<void> fromStatus(status_t status) {
- return (status == android::OK) ? HalResult<void>::ok() : fromFailedStatus<void>(status);
+ return (status == android::OK) ? HalResult<void>::ok()
+ : fromFailedStatus<void>(std::move(status));
}
- static HalResult<void> fromStatus(binder::Status status) {
- return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(status);
+ static HalResult<void> fromStatus(ndk::ScopedAStatus&& status) {
+ return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status));
}
- static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status) {
- return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<void>::ok()
- : fromFailedStatus<void>(status);
+ static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status&& status) {
+ return (status == hardware::vibrator::V1_0::Status::OK)
+ ? HalResult<void>::ok()
+ : fromFailedStatus<void>(std::move(status));
}
template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(ret);
+ static HalResult<void> fromReturn(hardware::Return<R>&& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(std::move(ret));
}
private:
@@ -146,21 +154,21 @@
}
template <typename T>
- static HalResult<T> fromFailedStatus(binder::Status status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
- status.transactionError() == android::UNKNOWN_TRANSACTION) {
- // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
- // the same as the operation being unsupported by this HAL. Should not retry.
+ static HalResult<T> fromFailedStatus(ndk::ScopedAStatus&& status) {
+ if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||
+ status.getStatus() == STATUS_UNKNOWN_TRANSACTION) {
+ // STATUS_UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this
+ // is the same as the operation being unsupported by this HAL. Should not retry.
return HalResult<T>::unsupported();
}
- if (status.exceptionCode() == binder::Status::EX_TRANSACTION_FAILED) {
- return HalResult<T>::transactionFailed(status.toString8().c_str());
+ if (status.getExceptionCode() == EX_TRANSACTION_FAILED) {
+ return HalResult<T>::transactionFailed(status.getMessage());
}
- return HalResult<T>::failed(status.toString8().c_str());
+ return HalResult<T>::failed(status.getMessage());
}
template <typename T>
- static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status status) {
+ static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status&& status) {
switch (status) {
case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION:
return HalResult<T>::unsupported();
@@ -171,7 +179,7 @@
}
template <typename T, typename R>
- static HalResult<T> fromFailedReturn(hardware::Return<R>& ret) {
+ static HalResult<T> fromFailedReturn(hardware::Return<R>&& ret) {
return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str())
: HalResult<T>::failed(ret.description().c_str());
}
@@ -179,14 +187,14 @@
// -------------------------------------------------------------------------------------------------
-class HalCallbackWrapper : public hardware::vibrator::BnVibratorCallback {
+class HalCallbackWrapper : public aidl::android::hardware::vibrator::BnVibratorCallback {
public:
HalCallbackWrapper(std::function<void()> completionCallback)
: mCompletionCallback(completionCallback) {}
- binder::Status onComplete() override {
+ ndk::ScopedAStatus onComplete() override {
mCompletionCallback();
- return binder::Status::ok();
+ return ndk::ScopedAStatus::ok();
}
private:
@@ -198,14 +206,15 @@
// Vibrator HAL capabilities.
enum class Capabilities : int32_t {
NONE = 0,
- ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
- PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
- AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
- EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
- EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
- COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
- COMPOSE_PWLE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS,
- ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL,
+ ON_CALLBACK = aidl::android::hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
+ PERFORM_CALLBACK = aidl::android::hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
+ AMPLITUDE_CONTROL = aidl::android::hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
+ EXTERNAL_CONTROL = aidl::android::hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
+ EXTERNAL_AMPLITUDE_CONTROL =
+ aidl::android::hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
+ COMPOSE_EFFECTS = aidl::android::hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
+ COMPOSE_PWLE_EFFECTS = aidl::android::hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS,
+ ALWAYS_ON_CONTROL = aidl::android::hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL,
};
inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
@@ -230,10 +239,15 @@
class Info {
public:
+ using Effect = aidl::android::hardware::vibrator::Effect;
+ using EffectStrength = aidl::android::hardware::vibrator::EffectStrength;
+ using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive;
+ using Braking = aidl::android::hardware::vibrator::Braking;
+
const HalResult<Capabilities> capabilities;
- const HalResult<std::vector<hardware::vibrator::Effect>> supportedEffects;
- const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking;
- const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives;
+ const HalResult<std::vector<Effect>> supportedEffects;
+ const HalResult<std::vector<Braking>> supportedBraking;
+ const HalResult<std::vector<CompositePrimitive>> supportedPrimitives;
const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations;
const HalResult<std::chrono::milliseconds> primitiveDelayMax;
const HalResult<std::chrono::milliseconds> pwlePrimitiveDurationMax;
@@ -244,15 +258,15 @@
const HalResult<float> frequencyResolution;
const HalResult<float> qFactor;
const HalResult<std::vector<float>> maxAmplitudes;
+ const HalResult<int32_t> maxEnvelopeEffectSize;
+ const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration;
+ const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration;
void logFailures() const {
logFailure<Capabilities>(capabilities, "getCapabilities");
- logFailure<std::vector<hardware::vibrator::Effect>>(supportedEffects,
- "getSupportedEffects");
- logFailure<std::vector<hardware::vibrator::Braking>>(supportedBraking,
- "getSupportedBraking");
- logFailure<std::vector<hardware::vibrator::CompositePrimitive>>(supportedPrimitives,
- "getSupportedPrimitives");
+ logFailure<std::vector<Effect>>(supportedEffects, "getSupportedEffects");
+ logFailure<std::vector<Braking>>(supportedBraking, "getSupportedBraking");
+ logFailure<std::vector<CompositePrimitive>>(supportedPrimitives, "getSupportedPrimitives");
logFailure<std::vector<std::chrono::milliseconds>>(primitiveDurations,
"getPrimitiveDuration");
logFailure<std::chrono::milliseconds>(primitiveDelayMax, "getPrimitiveDelayMax");
@@ -265,6 +279,11 @@
logFailure<float>(frequencyResolution, "getFrequencyResolution");
logFailure<float>(qFactor, "getQFactor");
logFailure<std::vector<float>>(maxAmplitudes, "getMaxAmplitudes");
+ logFailure<int32_t>(maxEnvelopeEffectSize, "getMaxEnvelopeEffectSize");
+ logFailure<std::chrono::milliseconds>(minEnvelopeEffectControlPointDuration,
+ "getMinEnvelopeEffectControlPointDuration");
+ logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration,
+ "getMaxEnvelopeEffectControlPointDuration");
}
bool shouldRetry() const {
@@ -274,7 +293,10 @@
pwlePrimitiveDurationMax.shouldRetry() || compositionSizeMax.shouldRetry() ||
pwleSizeMax.shouldRetry() || minFrequency.shouldRetry() ||
resonantFrequency.shouldRetry() || frequencyResolution.shouldRetry() ||
- qFactor.shouldRetry() || maxAmplitudes.shouldRetry();
+ qFactor.shouldRetry() || maxAmplitudes.shouldRetry() ||
+ maxEnvelopeEffectSize.shouldRetry() ||
+ minEnvelopeEffectControlPointDuration.shouldRetry() ||
+ maxEnvelopeEffectControlPointDuration.shouldRetry();
}
private:
@@ -302,19 +324,22 @@
mResonantFrequency,
mFrequencyResolution,
mQFactor,
- mMaxAmplitudes};
+ mMaxAmplitudes,
+ mMaxEnvelopeEffectSize,
+ mMinEnvelopeEffectControlPointDuration,
+ mMaxEnvelopeEffectControlPointDuration};
}
private:
// Create a transaction failed results as default so we can retry on the first time we get them.
static const constexpr char* MSG = "never loaded";
HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::transactionFailed(MSG);
- HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects =
- HalResult<std::vector<hardware::vibrator::Effect>>::transactionFailed(MSG);
- HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking =
- HalResult<std::vector<hardware::vibrator::Braking>>::transactionFailed(MSG);
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives =
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::transactionFailed(MSG);
+ HalResult<std::vector<Info::Effect>> mSupportedEffects =
+ HalResult<std::vector<Info::Effect>>::transactionFailed(MSG);
+ HalResult<std::vector<Info::Braking>> mSupportedBraking =
+ HalResult<std::vector<Info::Braking>>::transactionFailed(MSG);
+ HalResult<std::vector<Info::CompositePrimitive>> mSupportedPrimitives =
+ HalResult<std::vector<Info::CompositePrimitive>>::transactionFailed(MSG);
HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
HalResult<std::vector<std::chrono::milliseconds>>::transactionFailed(MSG);
HalResult<std::chrono::milliseconds> mPrimitiveDelayMax =
@@ -329,6 +354,11 @@
HalResult<float> mQFactor = HalResult<float>::transactionFailed(MSG);
HalResult<std::vector<float>> mMaxAmplitudes =
HalResult<std::vector<float>>::transactionFailed(MSG);
+ HalResult<int32_t> mMaxEnvelopeEffectSize = HalResult<int>::transactionFailed(MSG);
+ HalResult<std::chrono::milliseconds> mMinEnvelopeEffectControlPointDuration =
+ HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
+ HalResult<std::chrono::milliseconds> mMaxEnvelopeEffectControlPointDuration =
+ HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
friend class HalWrapper;
};
@@ -336,6 +366,16 @@
// Wrapper for Vibrator HAL handlers.
class HalWrapper {
public:
+ using Effect = aidl::android::hardware::vibrator::Effect;
+ using EffectStrength = aidl::android::hardware::vibrator::EffectStrength;
+ using VendorEffect = aidl::android::hardware::vibrator::VendorEffect;
+ using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive;
+ using CompositeEffect = aidl::android::hardware::vibrator::CompositeEffect;
+ using Braking = aidl::android::hardware::vibrator::Braking;
+ using PrimitivePwle = aidl::android::hardware::vibrator::PrimitivePwle;
+ using PwleV2Primitive = aidl::android::hardware::vibrator::PwleV2Primitive;
+ using PwleV2OutputMapEntry = aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+
explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
: mCallbackScheduler(std::move(scheduler)) {}
virtual ~HalWrapper() = default;
@@ -355,21 +395,25 @@
virtual HalResult<void> setAmplitude(float amplitude) = 0;
virtual HalResult<void> setExternalControl(bool enabled) = 0;
- virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
- hardware::vibrator::EffectStrength strength) = 0;
+ virtual HalResult<void> alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) = 0;
virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
virtual HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ Effect effect, EffectStrength strength,
const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> performVendorEffect(const VendorEffect& effect,
+ const std::function<void()>& completionCallback);
+
virtual HalResult<std::chrono::milliseconds> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitives,
+ const std::vector<CompositeEffect>& primitives,
const std::function<void()>& completionCallback);
- virtual HalResult<void> performPwleEffect(
- const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
- const std::function<void()>& completionCallback);
+ virtual HalResult<void> performPwleEffect(const std::vector<PrimitivePwle>& primitives,
+ const std::function<void()>& completionCallback);
+
+ virtual HalResult<void> composePwleV2(const std::vector<PwleV2Primitive>& composite,
+ const std::function<void()>& completionCallback);
protected:
// Shared pointer to allow CallbackScheduler to outlive this wrapper.
@@ -381,12 +425,11 @@
// Request vibrator info to HAL skipping cache.
virtual HalResult<Capabilities> getCapabilitiesInternal() = 0;
- virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
- virtual HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal();
- virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
- getSupportedPrimitivesInternal();
+ virtual HalResult<std::vector<Effect>> getSupportedEffectsInternal();
+ virtual HalResult<std::vector<Braking>> getSupportedBrakingInternal();
+ virtual HalResult<std::vector<CompositePrimitive>> getSupportedPrimitivesInternal();
virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
- const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives);
+ const std::vector<CompositePrimitive>& supportedPrimitives);
virtual HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal();
virtual HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal();
virtual HalResult<int32_t> getCompositionSizeMaxInternal();
@@ -396,6 +439,9 @@
virtual HalResult<float> getFrequencyResolutionInternal();
virtual HalResult<float> getQFactorInternal();
virtual HalResult<std::vector<float>> getMaxAmplitudesInternal();
+ virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal();
+ virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal();
+ virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal();
private:
std::mutex mInfoMutex;
@@ -405,12 +451,17 @@
// Wrapper for the AIDL Vibrator HAL.
class AidlHalWrapper : public HalWrapper {
public:
+ using IVibrator = aidl::android::hardware::vibrator::IVibrator;
+ using reconnect_fn = std::function<HalResult<std::shared_ptr<IVibrator>>()>;
+
AidlHalWrapper(
- std::shared_ptr<CallbackScheduler> scheduler, sp<hardware::vibrator::IVibrator> handle,
- std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> reconnectFn =
+ std::shared_ptr<CallbackScheduler> scheduler, std::shared_ptr<IVibrator> handle,
+ reconnect_fn reconnectFn =
[]() {
- return HalResult<sp<hardware::vibrator::IVibrator>>::ok(
- checkVintfService<hardware::vibrator::IVibrator>());
+ auto serviceName = std::string(IVibrator::descriptor) + "/default";
+ auto hal = IVibrator::fromBinder(
+ ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
+ return HalResult<std::shared_ptr<IVibrator>>::ok(std::move(hal));
})
: HalWrapper(std::move(scheduler)),
mReconnectFn(reconnectFn),
@@ -427,32 +478,36 @@
HalResult<void> setAmplitude(float amplitude) override final;
HalResult<void> setExternalControl(bool enabled) override final;
- HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
- hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnEnable(int32_t id, Effect effect,
+ EffectStrength strength) override final;
HalResult<void> alwaysOnDisable(int32_t id) override final;
HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ Effect effect, EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+ HalResult<void> performVendorEffect(
+ const VendorEffect& effect,
const std::function<void()>& completionCallback) override final;
HalResult<std::chrono::milliseconds> performComposedEffect(
- const std::vector<hardware::vibrator::CompositeEffect>& primitives,
+ const std::vector<CompositeEffect>& primitives,
const std::function<void()>& completionCallback) override final;
HalResult<void> performPwleEffect(
- const std::vector<hardware::vibrator::PrimitivePwle>& primitives,
+ const std::vector<PrimitivePwle>& primitives,
const std::function<void()>& completionCallback) override final;
+ HalResult<void> composePwleV2(const std::vector<PwleV2Primitive>& composite,
+ const std::function<void()>& completionCallback) override final;
+
protected:
HalResult<Capabilities> getCapabilitiesInternal() override final;
- HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal() override final;
- HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal()
- override final;
- HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal()
- override final;
+ HalResult<std::vector<Effect>> getSupportedEffectsInternal() override final;
+ HalResult<std::vector<Braking>> getSupportedBrakingInternal() override final;
+ HalResult<std::vector<CompositePrimitive>> getSupportedPrimitivesInternal() override final;
HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
- const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives)
- override final;
+ const std::vector<CompositePrimitive>& supportedPrimitives) override final;
HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal() override final;
HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal() override final;
HalResult<int32_t> getCompositionSizeMaxInternal() override final;
@@ -462,13 +517,20 @@
HalResult<float> getFrequencyResolutionInternal() override final;
HalResult<float> getQFactorInternal() override final;
HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
+ HalResult<int32_t> getMaxEnvelopeEffectSizeInternal() override final;
+
+ HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal()
+ override final;
+
+ HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal()
+ override final;
private:
- const std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> mReconnectFn;
+ const reconnect_fn mReconnectFn;
std::mutex mHandleMutex;
- sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
+ std::shared_ptr<IVibrator> mHandle GUARDED_BY(mHandleMutex);
- sp<hardware::vibrator::IVibrator> getHal();
+ std::shared_ptr<IVibrator> getHal();
};
// Wrapper for the HDIL Vibrator HALs.
@@ -489,8 +551,8 @@
HalResult<void> setAmplitude(float amplitude) override final;
virtual HalResult<void> setExternalControl(bool enabled) override;
- HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
- hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnEnable(int32_t id, HalWrapper::Effect effect,
+ HalWrapper::EffectStrength strength) override final;
HalResult<void> alwaysOnDisable(int32_t id) override final;
protected:
@@ -506,8 +568,7 @@
template <class T>
HalResult<std::chrono::milliseconds> performInternal(
- perform_fn<T> performFn, sp<I> handle, T effect,
- hardware::vibrator::EffectStrength strength,
+ perform_fn<T> performFn, sp<I> handle, T effect, HalWrapper::EffectStrength strength,
const std::function<void()>& completionCallback);
sp<I> getHal();
@@ -523,7 +584,7 @@
virtual ~HidlHalWrapperV1_0() = default;
HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
};
@@ -537,7 +598,7 @@
virtual ~HidlHalWrapperV1_1() = default;
HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
};
@@ -551,7 +612,7 @@
virtual ~HidlHalWrapperV1_2() = default;
HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
};
@@ -567,7 +628,7 @@
HalResult<void> setExternalControl(bool enabled) override final;
HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
protected:
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
index 9168565..70c846b 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
#define ANDROID_OS_VIBRATOR_MANAGER_HAL_CONTROLLER_H
-#include <android/hardware/vibrator/IVibratorManager.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
#include <vibratorservice/VibratorHalController.h>
#include <vibratorservice/VibratorManagerHalWrapper.h>
#include <unordered_map>
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
index 563f55e..9e3f221 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
#define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
-#include <android/hardware/vibrator/IVibratorManager.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
#include <vibratorservice/VibratorHalController.h>
#include <unordered_map>
@@ -28,14 +28,17 @@
// VibratorManager HAL capabilities.
enum class ManagerCapabilities : int32_t {
NONE = 0,
- SYNC = hardware::vibrator::IVibratorManager::CAP_SYNC,
- PREPARE_ON = hardware::vibrator::IVibratorManager::CAP_PREPARE_ON,
- PREPARE_PERFORM = hardware::vibrator::IVibratorManager::CAP_PREPARE_PERFORM,
- PREPARE_COMPOSE = hardware::vibrator::IVibratorManager::CAP_PREPARE_COMPOSE,
- MIXED_TRIGGER_ON = hardware::vibrator::IVibratorManager::IVibratorManager::CAP_MIXED_TRIGGER_ON,
- MIXED_TRIGGER_PERFORM = hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM,
- MIXED_TRIGGER_COMPOSE = hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE,
- TRIGGER_CALLBACK = hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK
+ SYNC = aidl::android::hardware::vibrator::IVibratorManager::CAP_SYNC,
+ PREPARE_ON = aidl::android::hardware::vibrator::IVibratorManager::CAP_PREPARE_ON,
+ PREPARE_PERFORM = aidl::android::hardware::vibrator::IVibratorManager::CAP_PREPARE_PERFORM,
+ PREPARE_COMPOSE = aidl::android::hardware::vibrator::IVibratorManager::CAP_PREPARE_COMPOSE,
+ MIXED_TRIGGER_ON = aidl::android::hardware::vibrator::IVibratorManager::IVibratorManager::
+ CAP_MIXED_TRIGGER_ON,
+ MIXED_TRIGGER_PERFORM =
+ aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM,
+ MIXED_TRIGGER_COMPOSE =
+ aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE,
+ TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK
};
inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) {
@@ -106,8 +109,10 @@
// Wrapper for the AIDL VibratorManager HAL.
class AidlManagerHalWrapper : public ManagerHalWrapper {
public:
+ using VibratorManager = aidl::android::hardware::vibrator::IVibratorManager;
+
explicit AidlManagerHalWrapper(std::shared_ptr<CallbackScheduler> callbackScheduler,
- sp<hardware::vibrator::IVibratorManager> handle)
+ std::shared_ptr<VibratorManager> handle)
: mHandle(std::move(handle)), mCallbackScheduler(callbackScheduler) {}
virtual ~AidlManagerHalWrapper() = default;
@@ -126,14 +131,14 @@
std::mutex mHandleMutex;
std::mutex mCapabilitiesMutex;
std::mutex mVibratorsMutex;
- sp<hardware::vibrator::IVibratorManager> mHandle GUARDED_BY(mHandleMutex);
+ std::shared_ptr<VibratorManager> mHandle GUARDED_BY(mHandleMutex);
std::optional<ManagerCapabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
std::optional<std::vector<int32_t>> mVibratorIds GUARDED_BY(mVibratorsMutex);
std::unordered_map<int32_t, std::shared_ptr<HalController>> mVibrators
GUARDED_BY(mVibratorsMutex);
std::shared_ptr<CallbackScheduler> mCallbackScheduler;
- sp<hardware::vibrator::IVibratorManager> getHal();
+ std::shared_ptr<VibratorManager> getHal();
std::shared_ptr<HalWrapper> connectToVibrator(int32_t vibratorId,
std::shared_ptr<CallbackScheduler> scheduler);
};
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index be71dc2..92527eb 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -44,12 +44,12 @@
],
shared_libs: [
"libbase",
- "libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libvibratorservice",
"libutils",
- "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator-V3-ndk",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 15fde91..f4c2898 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "VibratorHalControllerTest"
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <cutils/atomic.h>
#include <gmock/gmock.h>
@@ -29,10 +29,11 @@
#include <vibratorservice/VibratorHalController.h>
#include <vibratorservice/VibratorHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
using std::chrono::milliseconds;
@@ -46,41 +47,12 @@
// -------------------------------------------------------------------------------------------------
-class MockHalWrapper : public vibrator::HalWrapper {
-public:
- MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler)
- : HalWrapper(scheduler) {}
- virtual ~MockHalWrapper() = default;
-
- MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
- MOCK_METHOD(void, tryReconnect, (), (override));
- MOCK_METHOD(vibrator::HalResult<void>, on,
- (milliseconds timeout, const std::function<void()>& completionCallback),
- (override));
- MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
- MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (float amplitude), (override));
- MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
- MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
- (int32_t id, Effect effect, EffectStrength strength), (override));
- MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
- MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
- (Effect effect, EffectStrength strength,
- const std::function<void()>& completionCallback),
- (override));
- MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (),
- (override));
-
- vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
-};
-
-// -------------------------------------------------------------------------------------------------
-
class VibratorHalControllerTest : public Test {
public:
void SetUp() override {
mConnectCounter = 0;
auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
- mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
+ mMockHal = std::make_shared<StrictMock<vibrator::MockHalWrapper>>(callbackScheduler);
mController = std::make_unique<
vibrator::HalController>(std::move(callbackScheduler),
[&](std::shared_ptr<vibrator::CallbackScheduler>) {
@@ -92,7 +64,7 @@
protected:
int32_t mConnectCounter;
- std::shared_ptr<MockHalWrapper> mMockHal;
+ std::shared_ptr<vibrator::MockHalWrapper> mMockHal;
std::unique_ptr<vibrator::HalController> mController;
};
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 03c9e77..17f384d 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -16,7 +16,8 @@
#define LOG_TAG "VibratorHalWrapperAidlTest"
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/persistable_bundle_aidl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -27,18 +28,20 @@
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
-using android::binder::Status;
-
-using android::hardware::vibrator::Braking;
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::IVibrator;
-using android::hardware::vibrator::IVibratorCallback;
-using android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorCallback;
+using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2Primitive;
+using aidl::android::hardware::vibrator::VendorEffect;
+using aidl::android::os::PersistableBundle;
using namespace android;
using namespace std::chrono_literals;
@@ -46,61 +49,10 @@
// -------------------------------------------------------------------------------------------------
-class MockBinder : public BBinder {
-public:
- MOCK_METHOD(status_t, linkToDeath,
- (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
- MOCK_METHOD(status_t, unlinkToDeath,
- (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
- wp<DeathRecipient>* outRecipient),
- (override));
- MOCK_METHOD(status_t, pingBinder, (), (override));
-};
-
-class MockIVibrator : public IVibrator {
-public:
- MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
- MOCK_METHOD(Status, off, (), (override));
- MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
- MOCK_METHOD(Status, perform,
- (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
- (override));
- MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
- MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
- MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
- MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
- (override));
- MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
- MOCK_METHOD(Status, compose,
- (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
- (override));
- MOCK_METHOD(Status, composePwle,
- (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
- MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
- MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
- MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
- MOCK_METHOD(Status, getQFactor, (float * ret), (override));
- MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
- MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
- MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
- MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
- MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
class VibratorHalWrapperAidlTest : public Test {
public:
void SetUp() override {
- mMockBinder = new StrictMock<MockBinder>();
- mMockHal = new StrictMock<MockIVibrator>();
+ mMockHal = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>();
mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal);
ASSERT_NE(mWrapper, nullptr);
@@ -109,54 +61,28 @@
protected:
std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibrator>> mMockHal = nullptr;
- sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+ std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockHal = nullptr;
};
// -------------------------------------------------------------------------------------------------
-ACTION(TriggerCallbackInArg1) {
- if (arg1 != nullptr) {
- arg1->onComplete();
- }
-}
-
-ACTION(TriggerCallbackInArg2) {
- if (arg2 != nullptr) {
- arg2->onComplete();
- }
-}
-
-TEST_F(VibratorHalWrapperAidlTest, TestPing) {
- EXPECT_CALL(*mMockHal.get(), onAsBinder())
- .Times(Exactly(2))
- .WillRepeatedly(Return(mMockBinder.get()));
- EXPECT_CALL(*mMockBinder.get(), pingBinder())
- .Times(Exactly(2))
- .WillOnce(Return(android::OK))
- .WillRepeatedly(Return(android::DEAD_OBJECT));
-
- ASSERT_TRUE(mWrapper->ping().isOk());
- ASSERT_TRUE(mWrapper->ping().isFailed());
-}
-
TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), on(Eq(100), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -179,20 +105,20 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status()));
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
.Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ .WillOnce(vibrator::TriggerSchedulerCallback());
EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -211,10 +137,9 @@
TEST_F(VibratorHalWrapperAidlTest, TestOff) {
EXPECT_CALL(*mMockHal.get(), off())
.Times(Exactly(3))
- .WillOnce(Return(Status()))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::ok()))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
ASSERT_TRUE(mWrapper->off().isOk());
ASSERT_TRUE(mWrapper->off().isUnsupported());
@@ -224,13 +149,15 @@
TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f)))
+ .Times(Exactly(1))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
ASSERT_TRUE(mWrapper->setAmplitude(0.1f).isOk());
@@ -241,12 +168,13 @@
TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(1))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
.Times(Exactly(2))
- .WillOnce(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
@@ -259,15 +187,16 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(),
alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT);
@@ -281,14 +210,15 @@
TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
+ .Times(Exactly(1))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2)))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk());
@@ -305,72 +235,94 @@
constexpr int32_t PWLE_SIZE_MAX = 20;
constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
constexpr int32_t PWLE_DURATION_MAX = 200;
+ constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16;
+ constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
+ constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
std::vector<Braking> supportedBraking = {Braking::CLAB};
std::vector<float> amplitudes = {0.f, 1.f, 0.f};
std::vector<std::chrono::milliseconds> primitiveDurations;
- constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+ constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>();
constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
primitiveDurations.resize(primitiveCount);
primitiveDurations[static_cast<size_t>(CompositePrimitive::CLICK)] = 10ms;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(supportedEffects), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedBraking), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(supportedBraking), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(supportedPrimitives), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(10), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(F_MIN), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(F0), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getQFactor(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(Q_FACTOR), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(amplitudes), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(amplitudes), Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
+ Return(ndk::ScopedAStatus::ok())));
vibrator::Info failed = mWrapper->getInfo();
ASSERT_TRUE(failed.capabilities.isFailed());
@@ -387,6 +339,9 @@
ASSERT_TRUE(failed.frequencyResolution.isFailed());
ASSERT_TRUE(failed.qFactor.isFailed());
ASSERT_TRUE(failed.maxAmplitudes.isFailed());
+ ASSERT_TRUE(failed.maxEnvelopeEffectSize.isFailed());
+ ASSERT_TRUE(failed.minEnvelopeEffectControlPointDuration.isFailed());
+ ASSERT_TRUE(failed.maxEnvelopeEffectControlPointDuration.isFailed());
vibrator::Info successful = mWrapper->getInfo();
ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
@@ -404,6 +359,11 @@
ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
ASSERT_EQ(Q_FACTOR, successful.qFactor.value());
ASSERT_EQ(amplitudes, successful.maxAmplitudes.value());
+ ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, successful.maxEnvelopeEffectSize.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ successful.minEnvelopeEffectControlPointDuration.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
+ successful.maxEnvelopeEffectControlPointDuration.value());
}
TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
@@ -413,50 +373,65 @@
constexpr int32_t PWLE_SIZE_MAX = 20;
constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
constexpr int32_t PWLE_DURATION_MAX = 200;
+ constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16;
+ constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
+ constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(supportedEffects), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getQFactor(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status())));
+ .WillOnce(
+ DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(F_MIN), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(F0), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
.Times(Exactly(1))
- .WillRepeatedly(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
+ Return(ndk::ScopedAStatus::ok())));
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
@@ -480,6 +455,11 @@
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
ASSERT_TRUE(info.qFactor.isUnsupported());
ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+ ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, info.maxEnvelopeEffectSize.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ info.minEnvelopeEffectControlPointDuration.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
+ info.maxEnvelopeEffectControlPointDuration.value());
}
TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
@@ -487,18 +467,18 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
.Times(Exactly(1))
- .WillRepeatedly(
- DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<3>(1000), WithArg<2>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -525,21 +505,20 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<3>(10), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
.Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ .WillOnce(vibrator::TriggerSchedulerCallback());
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -560,6 +539,42 @@
ASSERT_EQ(1, *callbackCounter.get());
}
+TEST_F(VibratorHalWrapperAidlTest, TestPerformVendorEffect) {
+ PersistableBundle vendorData;
+ vendorData.putInt("key", 1);
+ VendorEffect vendorEffect;
+ vendorEffect.vendorData = vendorData;
+ vendorEffect.strength = EffectStrength::MEDIUM;
+ vendorEffect.scale = 0.5f;
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), performVendorEffect(_, _))
+ .Times(Exactly(3))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performVendorEffect(vendorEffect, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered on failure
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ result = mWrapper->performVendorEffect(vendorEffect, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ result = mWrapper->performVendorEffect(vendorEffect, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK,
CompositePrimitive::SPIN,
@@ -576,26 +591,28 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitives),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(1), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(3), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)));
EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -630,26 +647,32 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitives),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)));
EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(2), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
.Times(Exactly(2))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())))
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -680,12 +703,12 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), composePwle(Eq(emptyPrimitives), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(
- Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
EXPECT_CALL(*mMockHal.get(), composePwle(Eq(multiplePrimitives), _))
.Times(Exactly(2))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -705,3 +728,38 @@
ASSERT_TRUE(result.isOk());
ASSERT_EQ(1, *callbackCounter.get());
}
+
+TEST_F(VibratorHalWrapperAidlTest, TestComposePwleV2) {
+ auto pwleEffect = {
+ PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
+ PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
+ PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
+ };
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), composePwleV2(_, _))
+ .Times(Exactly(3))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(WithArg<1>(vibrator::TriggerCallback()),
+ Return(ndk::ScopedAStatus::ok())));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->composePwleV2(pwleEffect, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered on failure
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ result = mWrapper->composePwleV2(pwleEffect, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ result = mWrapper->composePwleV2(pwleEffect, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 0c27fc7..a09ddec 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -16,7 +16,8 @@
#define LOG_TAG "VibratorHalWrapperHidlV1_0Test"
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/persistable_bundle_aidl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -27,17 +28,21 @@
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
-using android::hardware::vibrator::Braking;
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::IVibrator;
-using android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2Primitive;
+using aidl::android::hardware::vibrator::VendorEffect;
+using aidl::android::os::PersistableBundle;
using namespace android;
using namespace std::chrono_literals;
@@ -215,6 +220,9 @@
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
ASSERT_TRUE(info.qFactor.isUnsupported());
ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+ ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
+ ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
+ ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
}
TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
@@ -248,6 +256,9 @@
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
ASSERT_TRUE(info.qFactor.isUnsupported());
ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+ ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
+ ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
+ ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
}
TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
@@ -316,6 +327,22 @@
ASSERT_EQ(0, *callbackCounter.get());
}
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformVendorEffectUnsupported) {
+ PersistableBundle vendorData; // empty
+ VendorEffect vendorEffect;
+ vendorEffect.vendorData = vendorData;
+ vendorEffect.strength = EffectStrength::LIGHT;
+ vendorEffect.scale = 1.0f;
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->performVendorEffect(vendorEffect, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
singleEffect.push_back(
@@ -349,3 +376,19 @@
// No callback is triggered.
ASSERT_EQ(0, *callbackCounter.get());
}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) {
+ auto pwleEffect = {
+ PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
+ PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
+ PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
+ };
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->composePwleV2(pwleEffect, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
index d887efc..b0a6537 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "VibratorHalWrapperHidlV1_1Test"
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -26,13 +26,14 @@
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
using namespace android;
using namespace std::chrono_literals;
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
index 26d9350..dfe3fa0 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "VibratorHalWrapperHidlV1_2Test"
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -26,14 +26,15 @@
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
using namespace android;
using namespace std::chrono_literals;
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index a6f1a74..8624332 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "VibratorHalWrapperHidlV1_3Test"
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -27,6 +27,7 @@
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
@@ -34,9 +35,9 @@
namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
using namespace android;
using namespace std::chrono_literals;
diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
index 11a8b66..c7214e0 100644
--- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
@@ -24,6 +24,7 @@
#include <vibratorservice/VibratorManagerHalController.h>
+#include "test_mocks.h"
#include "test_utils.h"
using android::vibrator::HalController;
@@ -35,6 +36,8 @@
static const std::vector<int32_t> VIBRATOR_IDS = {1, 2};
static constexpr int VIBRATOR_ID = 1;
+// -------------------------------------------------------------------------------------------------
+
class MockManagerHalWrapper : public vibrator::ManagerHalWrapper {
public:
MOCK_METHOD(void, tryReconnect, (), (override));
@@ -51,6 +54,8 @@
MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override));
};
+// -------------------------------------------------------------------------------------------------
+
class VibratorManagerHalControllerTest : public Test {
public:
void SetUp() override {
@@ -106,6 +111,8 @@
}
};
+// -------------------------------------------------------------------------------------------------
+
TEST_F(VibratorManagerHalControllerTest, TestInit) {
mController->init();
ASSERT_EQ(1, mConnectCounter);
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index dffc281..764d9be 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -23,84 +23,42 @@
#include <vibratorservice/VibratorManagerHalWrapper.h>
+#include "test_mocks.h"
#include "test_utils.h"
-using android::binder::Status;
-
-using android::hardware::vibrator::Braking;
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::IVibrator;
-using android::hardware::vibrator::IVibratorCallback;
-using android::hardware::vibrator::IVibratorManager;
-using android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorCallback;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::PrimitivePwle;
using namespace android;
using namespace testing;
static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); };
-class MockBinder : public BBinder {
-public:
- MOCK_METHOD(status_t, linkToDeath,
- (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
- MOCK_METHOD(status_t, unlinkToDeath,
- (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
- wp<DeathRecipient>* outRecipient),
- (override));
- MOCK_METHOD(status_t, pingBinder, (), (override));
-};
-
-class MockIVibrator : public IVibrator {
-public:
- MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
- MOCK_METHOD(Status, off, (), (override));
- MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
- MOCK_METHOD(Status, perform,
- (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
- (override));
- MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
- MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
- MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
- MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
- (override));
- MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
- MOCK_METHOD(Status, compose,
- (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
- (override));
- MOCK_METHOD(Status, composePwle,
- (const std::vector<PrimitivePwle>& e, const sp<IVibratorCallback>& cb), (override));
- MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
- MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
- MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
- MOCK_METHOD(Status, getQFactor, (float * ret), (override));
- MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override));
- MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override));
- MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override));
- MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override));
- MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override));
- MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
-};
+// -------------------------------------------------------------------------------------------------
class MockIVibratorManager : public IVibratorManager {
public:
- MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
- MOCK_METHOD(Status, getVibratorIds, (std::vector<int32_t> * ret), (override));
- MOCK_METHOD(Status, getVibrator, (int32_t id, sp<IVibrator>* ret), (override));
- MOCK_METHOD(Status, prepareSynced, (const std::vector<int32_t>& ids), (override));
- MOCK_METHOD(Status, triggerSynced, (const sp<IVibratorCallback>& cb), (override));
- MOCK_METHOD(Status, cancelSynced, (), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MockIVibratorManager() = default;
+
+ MOCK_METHOD(ndk::ScopedAStatus, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getVibratorIds, (std::vector<int32_t> * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getVibrator, (int32_t id, std::shared_ptr<IVibrator>* ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, prepareSynced, (const std::vector<int32_t>& ids), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, triggerSynced, (const std::shared_ptr<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, cancelSynced, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
};
// -------------------------------------------------------------------------------------------------
@@ -108,9 +66,8 @@
class VibratorManagerHalWrapperAidlTest : public Test {
public:
void SetUp() override {
- mMockBinder = new StrictMock<MockBinder>();
- mMockVibrator = new StrictMock<MockIVibrator>();
- mMockHal = new StrictMock<MockIVibratorManager>();
+ mMockVibrator = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>();
+ mMockHal = ndk::SharedRefBase::make<StrictMock<MockIVibratorManager>>();
mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal);
ASSERT_NE(mWrapper, nullptr);
@@ -119,9 +76,8 @@
protected:
std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorManager>> mMockHal = nullptr;
- sp<StrictMock<MockIVibrator>> mMockVibrator = nullptr;
- sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+ std::shared_ptr<StrictMock<MockIVibratorManager>> mMockHal = nullptr;
+ std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator = nullptr;
};
// -------------------------------------------------------------------------------------------------
@@ -129,32 +85,13 @@
static const std::vector<int32_t> kVibratorIds = {1, 2};
static constexpr int kVibratorId = 1;
-ACTION(TriggerCallback) {
- if (arg0 != nullptr) {
- arg0->onComplete();
- }
-}
-
-TEST_F(VibratorManagerHalWrapperAidlTest, TestPing) {
- EXPECT_CALL(*mMockHal.get(), onAsBinder())
- .Times(Exactly(2))
- .WillRepeatedly(Return(mMockBinder.get()));
- EXPECT_CALL(*mMockBinder.get(), pingBinder())
- .Times(Exactly(2))
- .WillOnce(Return(android::OK))
- .WillRepeatedly(Return(android::DEAD_OBJECT));
-
- ASSERT_TRUE(mWrapper->ping().isOk());
- ASSERT_TRUE(mWrapper->ping().isFailed());
-}
-
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC),
+ Return(ndk::ScopedAStatus::ok())));
ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
@@ -167,7 +104,8 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC),
+ Return(ndk::ScopedAStatus::ok())));
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
@@ -187,10 +125,9 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsDoesNotCacheFailedResult) {
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
ASSERT_TRUE(mWrapper->getVibratorIds().isUnsupported());
ASSERT_TRUE(mWrapper->getVibratorIds().isFailed());
@@ -203,7 +140,7 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorIdsCachesResult) {
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
@@ -225,11 +162,11 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())));
}
auto result = mWrapper->getVibrator(kVibratorId);
@@ -241,7 +178,7 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorWithInvalidIdFails) {
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
ASSERT_TRUE(mWrapper->getVibrator(0).isFailed());
}
@@ -249,20 +186,21 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer) {
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _))
.Times(Exactly(3))
.WillOnce(DoAll(SetArgPointee<1>(nullptr),
- Return(Status::fromExceptionCode(
- Status::Exception::EX_TRANSACTION_FAILED))))
- .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+ Return(ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED))))
+ // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())))
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockVibrator.get(), off())
.Times(Exactly(3))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED)))
- .WillRepeatedly(Return(Status()));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED)))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
// Get vibrator controller is successful even if first getVibrator.
auto result = mWrapper->getVibrator(kVibratorId);
@@ -281,18 +219,19 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestPrepareSynced) {
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getVibrator(_, _))
.Times(Exactly(2))
- .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+ // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())))
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), prepareSynced(Eq(kVibratorIds)))
.Times(Exactly(3))
- .WillOnce(
- Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(Return(Status()));
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
ASSERT_TRUE(mWrapper->getVibratorIds().isOk());
ASSERT_TRUE(mWrapper->prepareSynced(kVibratorIds).isUnsupported());
@@ -305,13 +244,13 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(IVibratorManager::CAP_TRIGGER_CALLBACK),
- Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_TRIGGER_CALLBACK),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
.Times(Exactly(3))
- .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(DoAll(TriggerCallback(), Return(Status())));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(vibrator::TriggerCallback(), Return(ndk::ScopedAStatus::ok())));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -328,11 +267,11 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
- .WillRepeatedly(
- DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(IVibratorManager::CAP_SYNC),
+ Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), triggerSynced(Eq(nullptr)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status()));
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
@@ -345,9 +284,9 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) {
EXPECT_CALL(*mMockHal.get(), cancelSynced())
.Times(Exactly(3))
- .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
- .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
- .WillRepeatedly(Return(Status()));
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
ASSERT_TRUE(mWrapper->cancelSynced().isFailed());
@@ -357,13 +296,17 @@
TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSyncedReloadsAllControllers) {
EXPECT_CALL(*mMockHal.get(), getVibratorIds(_))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<0>(kVibratorIds), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<0>(kVibratorIds), Return(ndk::ScopedAStatus::ok())));
EXPECT_CALL(*mMockHal.get(), getVibrator(_, _))
.Times(Exactly(2))
- .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
+ // ndk::ScopedAStatus::ok() cannot be copy-constructed so can't use WillRepeatedly
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())))
+ .WillOnce(DoAll(SetArgPointee<1>(mMockVibrator), Return(ndk::ScopedAStatus::ok())));
- EXPECT_CALL(*mMockHal.get(), cancelSynced()).Times(Exactly(1)).WillRepeatedly(Return(Status()));
+ EXPECT_CALL(*mMockHal.get(), cancelSynced())
+ .Times(Exactly(1))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
ASSERT_TRUE(mWrapper->getVibratorIds().isOk());
ASSERT_TRUE(mWrapper->cancelSynced().isOk());
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
index 0850ef3..7877236 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -23,10 +23,12 @@
#include <vibratorservice/VibratorManagerHalWrapper.h>
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
+#include "test_mocks.h"
+
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
using std::chrono::milliseconds;
@@ -35,27 +37,16 @@
// -------------------------------------------------------------------------------------------------
-class MockHalController : public vibrator::HalController {
-public:
- MockHalController() = default;
- virtual ~MockHalController() = default;
-
- MOCK_METHOD(bool, init, (), (override));
- MOCK_METHOD(void, tryReconnect, (), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
class VibratorManagerHalWrapperLegacyTest : public Test {
public:
void SetUp() override {
- mMockController = std::make_shared<StrictMock<MockHalController>>();
+ mMockController = std::make_shared<StrictMock<vibrator::MockHalController>>();
mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController);
ASSERT_NE(mWrapper, nullptr);
}
protected:
- std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr;
+ std::shared_ptr<StrictMock<vibrator::MockHalController>> mMockController = nullptr;
std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
};
diff --git a/services/vibratorservice/test/test_mocks.h b/services/vibratorservice/test/test_mocks.h
new file mode 100644
index 0000000..5e09084
--- /dev/null
+++ b/services/vibratorservice/test/test_mocks.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef VIBRATORSERVICE_UNITTEST_MOCKS_H_
+#define VIBRATORSERVICE_UNITTEST_MOCKS_H_
+
+#include <gmock/gmock.h>
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+using std::chrono::milliseconds;
+
+using namespace testing;
+
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorCallback;
+using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+using aidl::android::hardware::vibrator::PwleV2Primitive;
+using aidl::android::hardware::vibrator::VendorEffect;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibrator : public IVibrator {
+public:
+ MockIVibrator() = default;
+
+ MOCK_METHOD(ndk::ScopedAStatus, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, off, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, on,
+ (int32_t timeout, const std::shared_ptr<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, perform,
+ (Effect e, EffectStrength s, const std::shared_ptr<IVibratorCallback>& cb,
+ int32_t* ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, performVendorEffect,
+ (const VendorEffect& e, const std::shared_ptr<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportedEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCompositionDelayMax, (int32_t * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, compose,
+ (const std::vector<CompositeEffect>& e,
+ const std::shared_ptr<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, composePwle,
+ (const std::vector<PrimitivePwle>& e, const std::shared_ptr<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getQFactor, (float* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getResonantFrequency, (float* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getFrequencyResolution, (float* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getFrequencyMinimum, (float* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getBandwidthAmplitudeMap, (std::vector<float> * ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPwlePrimitiveDurationMax, (int32_t * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPwleCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportedBraking, (std::vector<Braking> * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPwleV2FrequencyToOutputAccelerationMap,
+ (std::vector<PwleV2OutputMapEntry> * ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPwleV2PrimitiveDurationMaxMillis, (int32_t* ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPwleV2PrimitiveDurationMinMillis, (int32_t* ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getPwleV2CompositionSizeMax, (int32_t* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, composePwleV2,
+ (const std::vector<PwleV2Primitive>& e,
+ const std::shared_ptr<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+};
+
+// gmock requirement to provide a WithArg<0>(TriggerCallback()) matcher
+typedef void TriggerCallbackFunction(const std::shared_ptr<IVibratorCallback>&);
+
+class TriggerCallbackAction : public ActionInterface<TriggerCallbackFunction> {
+public:
+ explicit TriggerCallbackAction() {}
+
+ virtual Result Perform(const ArgumentTuple& args) {
+ const std::shared_ptr<IVibratorCallback>& callback = get<0>(args);
+ if (callback) {
+ callback->onComplete();
+ }
+ }
+};
+
+inline Action<TriggerCallbackFunction> TriggerCallback() {
+ return MakeAction(new TriggerCallbackAction());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class MockCallbackScheduler : public CallbackScheduler {
+public:
+ MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
+ (override));
+};
+
+ACTION(TriggerSchedulerCallback) {
+ arg0();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalWrapper : public HalWrapper {
+public:
+ MockHalWrapper(std::shared_ptr<CallbackScheduler> scheduler) : HalWrapper(scheduler) {}
+ virtual ~MockHalWrapper() = default;
+
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, on,
+ (milliseconds timeout, const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
+ (int32_t id, Effect effect, EffectStrength strength), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
+ (Effect effect, EffectStrength strength,
+ const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (),
+ (override));
+
+ CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
+};
+
+class MockHalController : public vibrator::HalController {
+public:
+ MockHalController() = default;
+ virtual ~MockHalController() = default;
+
+ MOCK_METHOD(bool, init, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace vibrator
+
+} // namespace android
+
+#endif // VIBRATORSERVICE_UNITTEST_MOCKS_H_
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index 715c221..e99965c 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -17,7 +17,7 @@
#ifndef VIBRATORSERVICE_UNITTEST_UTIL_H_
#define VIBRATORSERVICE_UNITTEST_UTIL_H_
-#include <android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <vibratorservice/VibratorHalWrapper.h>
@@ -25,24 +25,12 @@
namespace vibrator {
-using ::android::hardware::vibrator::ActivePwle;
-using ::android::hardware::vibrator::Braking;
-using ::android::hardware::vibrator::BrakingPwle;
-using ::android::hardware::vibrator::CompositeEffect;
-using ::android::hardware::vibrator::CompositePrimitive;
-using ::android::hardware::vibrator::PrimitivePwle;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockCallbackScheduler : public vibrator::CallbackScheduler {
-public:
- MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
- (override));
-};
-
-ACTION(TriggerSchedulerCallback) {
- arg0();
-}
+using aidl::android::hardware::vibrator::ActivePwle;
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::BrakingPwle;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::PrimitivePwle;
// -------------------------------------------------------------------------------------------------
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index c8ff76a..879d2d0 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -29,6 +29,18 @@
unversioned_until: "current",
}
+aconfig_declarations {
+ name: "libvulkan_flags",
+ package: "com.android.graphics.libvulkan.flags",
+ container: "system",
+ srcs: ["libvulkan_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "libvulkanflags",
+ aconfig_declarations: "libvulkan_flags",
+}
+
cc_library_shared {
name: "libvulkan",
llndk: {
@@ -110,5 +122,8 @@
"android.hardware.graphics.common@1.0",
"libSurfaceFlingerProp",
],
- static_libs: ["libgrallocusage"],
+ static_libs: [
+ "libgrallocusage",
+ "libvulkanflags",
+ ],
}
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index a9706bc..9ff0b46 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -25,6 +25,9 @@
#undef VK_NO_PROTOTYPES
#include "api.h"
+/*
+ * This file is autogenerated by api_generator.py. Do not edit directly.
+ */
namespace vulkan {
namespace api {
@@ -178,7 +181,7 @@
INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
- INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, instance, GetPhysicalDevicePresentRectanglesKHR);
INIT_PROC(false, instance, GetPhysicalDeviceToolProperties);
// clang-format on
@@ -325,9 +328,9 @@
INIT_PROC(false, dev, BindBufferMemory2);
INIT_PROC(false, dev, BindImageMemory2);
INIT_PROC(false, dev, CmdSetDeviceMask);
- INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupPresentCapabilitiesKHR);
- INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupSurfacePresentModesKHR);
- INIT_PROC_EXT(KHR_swapchain, false, dev, AcquireNextImage2KHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, GetDeviceGroupPresentCapabilitiesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, GetDeviceGroupSurfacePresentModesKHR);
+ INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImage2KHR);
INIT_PROC(false, dev, CmdDispatchBase);
INIT_PROC(false, dev, CreateDescriptorUpdateTemplate);
INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate);
@@ -659,6 +662,8 @@
"vkGetDrmDisplayEXT",
"vkGetInstanceProcAddr",
"vkGetPhysicalDeviceCalibrateableTimeDomainsEXT",
+ "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR",
+ "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR",
"vkGetPhysicalDeviceDisplayPlaneProperties2KHR",
"vkGetPhysicalDeviceDisplayProperties2KHR",
"vkGetPhysicalDeviceExternalBufferProperties",
@@ -703,6 +708,7 @@
"vkGetPhysicalDeviceToolProperties",
"vkGetPhysicalDeviceToolPropertiesEXT",
"vkGetPhysicalDeviceVideoCapabilitiesKHR",
+ "vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR",
"vkGetPhysicalDeviceVideoFormatPropertiesKHR",
"vkSubmitDebugUtilsMessageEXT",
};
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 4998018..b468a89 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -25,6 +25,9 @@
#include "driver_gen.h"
+/*
+ * This file is autogenerated by api_generator.py. Do not edit directly.
+ */
namespace vulkan {
namespace api {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index ef213f0..01436db 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -41,10 +41,12 @@
#include <new>
#include <vector>
+#include <com_android_graphics_libvulkan_flags.h>
#include "stubhal.h"
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using namespace com::android::graphics::libvulkan;
extern "C" android_namespace_t* android_get_exported_namespace(const char*);
@@ -688,6 +690,7 @@
case ProcHook::KHR_incremental_present:
case ProcHook::KHR_shared_presentable_image:
case ProcHook::KHR_swapchain:
+ case ProcHook::KHR_swapchain_mutable_format:
case ProcHook::EXT_hdr_metadata:
case ProcHook::EXT_swapchain_maintenance1:
case ProcHook::ANDROID_external_memory_android_hardware_buffer:
@@ -740,6 +743,7 @@
break;
case ProcHook::ANDROID_external_memory_android_hardware_buffer:
case ProcHook::KHR_external_fence_fd:
+ case ProcHook::KHR_swapchain_mutable_format:
case ProcHook::EXTENSION_UNKNOWN:
// Extensions we don't need to do anything about at this level
break;
@@ -1251,6 +1255,15 @@
VK_EXT_SWAPCHAIN_MAINTENANCE_1_SPEC_VERSION});
}
+ VkPhysicalDeviceProperties pDeviceProperties;
+ data.driver.GetPhysicalDeviceProperties(physicalDevice, &pDeviceProperties);
+ if (flags::swapchain_mutable_format_ext() &&
+ pDeviceProperties.apiVersion >= VK_API_VERSION_1_2) {
+ loader_extensions.push_back(
+ {VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
+ VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION});
+ }
+
// enumerate our extensions first
if (!pLayerName && pProperties) {
uint32_t count = std::min(
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 8f09008..f741977 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -26,6 +26,9 @@
namespace vulkan {
namespace driver {
+/*
+ * This file is autogenerated by driver_generator.py. Do not edit directly.
+ */
namespace {
// clang-format off
@@ -613,6 +616,7 @@
if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities;
if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities;
if (strcmp(name, "VK_KHR_external_fence_fd") == 0) return ProcHook::KHR_external_fence_fd;
+ if (strcmp(name, "VK_KHR_swapchain_mutable_format") == 0) return ProcHook::KHR_swapchain_mutable_format;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 4527214..649c0f1 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -26,6 +26,9 @@
#include <optional>
#include <vector>
+/*
+ * This file is autogenerated by driver_generator.py. Do not edit directly.
+ */
namespace vulkan {
namespace driver {
@@ -59,6 +62,7 @@
KHR_external_semaphore_capabilities,
KHR_external_fence_capabilities,
KHR_external_fence_fd,
+ KHR_swapchain_mutable_format,
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
diff --git a/vulkan/libvulkan/libvulkan_flags.aconfig b/vulkan/libvulkan/libvulkan_flags.aconfig
new file mode 100644
index 0000000..891bc02
--- /dev/null
+++ b/vulkan/libvulkan/libvulkan_flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.graphics.libvulkan.flags"
+container: "system"
+
+flag {
+ name: "swapchain_mutable_format_ext"
+ namespace: "core_graphics"
+ description: "Enable the VK_KHR_swapchain_mutable_format vulkan extension"
+ bug: "341978292"
+ is_fixed_read_only: true
+}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index f01d1d9..09b0a14 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1472,6 +1472,12 @@
.flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
};
+ // If supporting mutable format swapchain add the mutable format flag
+ if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
+ image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR;
+ }
+
VkAndroidHardwareBufferUsageANDROID ahb_usage;
ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
ahb_usage.pNext = nullptr;
@@ -1480,23 +1486,14 @@
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;
- }
+ VkResult result = 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;
}
// Determine if USAGE_FRONT_BUFFER is needed.
@@ -1899,6 +1896,11 @@
num_images = 1;
}
+ VkImageFormatListCreateInfo extra_mutable_formats = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
+ };
+ VkImageFormatListCreateInfo* extra_mutable_formats_ptr;
+
// 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
@@ -1917,7 +1919,29 @@
image_compression.pNext = nullptr;
usage_info_pNext = &image_compression;
} break;
-
+ case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: {
+ const VkImageFormatListCreateInfo* format_list =
+ reinterpret_cast<const VkImageFormatListCreateInfo*>(
+ create_infos);
+ if (create_info->flags &
+ VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
+ if (format_list && format_list->viewFormatCount > 0 &&
+ format_list->pViewFormats) {
+ extra_mutable_formats.viewFormatCount =
+ format_list->viewFormatCount;
+ extra_mutable_formats.pViewFormats =
+ format_list->pViewFormats;
+ extra_mutable_formats_ptr = &extra_mutable_formats;
+ } else {
+ ALOGE(
+ "vk_swapchain_create_mutable_format_bit_khr was "
+ "set during swapchain creation but no valid "
+ "vkimageformatlistcreateinfo was found in the "
+ "pnext chain");
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ }
+ } break;
default:
// Ignore all other info structs
break;
@@ -2013,6 +2037,11 @@
.pQueueFamilyIndices = create_info->pQueueFamilyIndices,
};
+ if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
+ image_create.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ image_create.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR;
+ }
+
// Note: don't do deferred allocation for shared present modes. There's only one buffer
// involved so very little benefit.
if ((create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT) &&
@@ -2022,7 +2051,7 @@
// AcquireNextImage.
VkImageSwapchainCreateInfoKHR image_swapchain_create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR,
- .pNext = nullptr,
+ .pNext = extra_mutable_formats_ptr,
.swapchain = HandleFromSwapchain(swapchain),
};
image_create.pNext = &image_swapchain_create;
@@ -2074,6 +2103,11 @@
ANativeWindowBuffer_getHardwareBuffer(img.buffer.get());
image_create.pNext = &image_native_buffer;
+ if (extra_mutable_formats_ptr) {
+ extra_mutable_formats_ptr->pNext = image_create.pNext;
+ image_create.pNext = extra_mutable_formats_ptr;
+ }
+
ATRACE_BEGIN("CreateImage");
result =
dispatch.CreateImage(device, &image_create, nullptr, &img.image);
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index d34851e..40a45af 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -24,6 +24,9 @@
using namespace null_driver;
+/*
+ * This file is autogenerated by null_generator.py. Do not edit directly.
+ */
namespace {
struct NameProc {
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index fb3bd05..0d1e223 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -22,6 +22,9 @@
#include <vulkan/vk_android_native_buffer.h>
#include <vulkan/vulkan.h>
+/*
+ * This file is autogenerated by null_generator.py. Do not edit directly.
+ */
namespace null_driver {
PFN_vkVoidFunction GetGlobalProcAddr(const char* name);
diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py
index be24172..001af20 100644
--- a/vulkan/scripts/api_generator.py
+++ b/vulkan/scripts/api_generator.py
@@ -61,6 +61,9 @@
#include "driver_gen.h"
+/*
+ * This file is autogenerated by api_generator.py. Do not edit directly.
+ */
namespace vulkan {
namespace api {
@@ -283,6 +286,9 @@
#undef VK_NO_PROTOTYPES
#include "api.h"
+/*
+ * This file is autogenerated by api_generator.py. Do not edit directly.
+ */
namespace vulkan {
namespace api {
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 48c0ae9..6159599 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -49,6 +49,7 @@
'VK_KHR_external_semaphore_capabilities',
'VK_KHR_external_fence_capabilities',
'VK_KHR_external_fence_fd',
+ 'VK_KHR_swapchain_mutable_format',
]
# Functions needed at vulkan::driver level.
@@ -224,6 +225,9 @@
#include <optional>
#include <vector>
+/*
+ * This file is autogenerated by driver_generator.py. Do not edit directly.
+ */
namespace vulkan {
namespace driver {
@@ -503,6 +507,9 @@
namespace vulkan {
namespace driver {
+/*
+ * This file is autogenerated by driver_generator.py. Do not edit directly.
+ */
namespace {
// clang-format off\n\n""")
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 866c1b7..6b4cbad 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -351,9 +351,50 @@
'external', 'vulkan-headers', 'registry', 'vk.xml')
tree = element_tree.parse(registry)
root = tree.getroot()
+
+ for exts in root.iter('extensions'):
+ for extension in exts.iter('extension'):
+ if 'vulkan' not in extension.get('supported').split(','):
+ # ANDROID_native_buffer is a weird special case -- it's declared in vk.xml as
+ # disabled but we _do_ want to generate plumbing for it in the Android loader.
+ if extension.get('name') != 'VK_ANDROID_native_buffer':
+ print('skip extension disabled or not for vulkan: ' + extension.get('name'))
+ continue
+
+ apiversion = 'VK_VERSION_1_0'
+ if extension.tag == 'extension':
+ extname = extension.get('name')
+ if (extension.get('type') == 'instance' and
+ extension.get('promotedto') is not None):
+ promoted_inst_ext_dict[extname] = \
+ version_2_api_version(extension.get('promotedto'))
+ for req in extension.iter('require'):
+ if req.get('feature') is not None:
+ apiversion = req.get('feature')
+ for commands in req:
+ if commands.tag == 'command':
+ cmd_name = commands.get('name')
+ if cmd_name not in extension_dict:
+ extension_dict[cmd_name] = extname
+ version_dict[cmd_name] = apiversion
+
+ for feature in root.iter('feature'):
+ if 'vulkan' not in feature.get('api').split(','):
+ continue
+
+ apiversion = feature.get('name')
+ for req in feature.iter('require'):
+ for command in req:
+ if command.tag == 'command':
+ cmd_name = command.get('name')
+ version_dict[cmd_name] = apiversion
+
for commands in root.iter('commands'):
for command in commands:
if command.tag == 'command':
+ if command.get('api') == 'vulkansc':
+ continue
+
parameter_list = []
protoset = False
cmd_name = ''
@@ -361,12 +402,18 @@
if command.get('alias') is not None:
alias = command.get('alias')
cmd_name = command.get('name')
- alias_dict[cmd_name] = alias
- command_list.append(cmd_name)
- param_dict[cmd_name] = param_dict[alias].copy()
- return_type_dict[cmd_name] = return_type_dict[alias]
+ # At this stage all valid commands have been added to the version
+ # dict so we can use it to filter valid commands
+ if cmd_name in version_dict:
+ alias_dict[cmd_name] = alias
+ command_list.append(cmd_name)
+ param_dict[cmd_name] = param_dict[alias].copy()
+ return_type_dict[cmd_name] = return_type_dict[alias]
for params in command:
if params.tag == 'param':
+ if params.get('api') == 'vulkansc':
+ # skip SC-only param variant
+ continue
param_type = ''
if params.text is not None and params.text.strip():
param_type = params.text.strip() + ' '
@@ -387,39 +434,13 @@
cmd_type = c.text
if c.tag == 'name':
cmd_name = c.text
- protoset = True
- command_list.append(cmd_name)
- return_type_dict[cmd_name] = cmd_type
+ if cmd_name in version_dict:
+ protoset = True
+ command_list.append(cmd_name)
+ return_type_dict[cmd_name] = cmd_type
if protoset:
param_dict[cmd_name] = parameter_list.copy()
- for exts in root.iter('extensions'):
- for extension in exts:
- apiversion = 'VK_VERSION_1_0'
- if extension.tag == 'extension':
- extname = extension.get('name')
- if (extension.get('type') == 'instance' and
- extension.get('promotedto') is not None):
- promoted_inst_ext_dict[extname] = \
- version_2_api_version(extension.get('promotedto'))
- for req in extension:
- if req.get('feature') is not None:
- apiversion = req.get('feature')
- for commands in req:
- if commands.tag == 'command':
- cmd_name = commands.get('name')
- if cmd_name not in extension_dict:
- extension_dict[cmd_name] = extname
- version_dict[cmd_name] = apiversion
-
- for feature in root.iter('feature'):
- apiversion = feature.get('name')
- for req in feature:
- for command in req:
- if command.tag == 'command':
- cmd_name = command.get('name')
- if cmd_name in command_list:
- version_dict[cmd_name] = apiversion
version_code_set = set()
for version in version_dict.values():
diff --git a/vulkan/scripts/null_generator.py b/vulkan/scripts/null_generator.py
index e9faef6..5c5bea3 100644
--- a/vulkan/scripts/null_generator.py
+++ b/vulkan/scripts/null_generator.py
@@ -55,6 +55,9 @@
#include <vulkan/vk_android_native_buffer.h>
#include <vulkan/vulkan.h>
+/*
+ * This file is autogenerated by null_generator.py. Do not edit directly.
+ */
namespace null_driver {
PFN_vkVoidFunction GetGlobalProcAddr(const char* name);
@@ -89,12 +92,17 @@
f.write(gencom.copyright_and_warning(2015))
f.write("""\
+#include <android/hardware_buffer.h>
+
#include <algorithm>
#include "null_driver_gen.h"
using namespace null_driver;
+/*
+ * This file is autogenerated by null_generator.py. Do not edit directly.
+ */
namespace {
struct NameProc {