Merge "Fix transactionCallbackThunk lifetime issue" into main
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/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 55e0583..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) {
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index 5980d5d..2892137 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -18,6 +18,7 @@
#include <chrono>
#include <optional>
+#include <vector>
#include <input/Input.h>
#include <input/InputTransport.h>
@@ -71,7 +72,15 @@
struct Sample {
std::chrono::nanoseconds eventTime;
- Pointer pointer;
+ 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;
+ }
};
/**
@@ -88,12 +97,21 @@
RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
/**
- * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. (If
+ * 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.)
+ * 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
@@ -125,4 +143,4 @@
inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index f5d7e66..8f3839f 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -57,8 +57,6 @@
return mTheRealServiceManager->getInterfaceDescriptor();
}
- IBinder* onAsBinder() override { return IInterface::asBinder(mTheRealServiceManager).get(); }
-
private:
sp<os::IServiceManager> mTheRealServiceManager;
void toBinderService(const os::Service& in, os::Service* _out);
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 2929bce..72d255e 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -771,7 +771,7 @@
* This provides a per-process-unique total ordering of binders where a null
* AIBinder* object is considered to be before all other binder objects.
* For instance, two binders refer to the same object in a local or remote
- * process when both AIBinder_lt(a, b) and AIBinder(b, a) are false. This API
+ * process when both AIBinder_lt(a, b) and AIBinder_lt(b, a) are false. This API
* might be used to insert and lookup binders in binary search trees.
*
* AIBinder* pointers themselves actually also create a per-process-unique total
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index 68528e1..6aff994 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -47,8 +47,11 @@
* be called once before startThreadPool. The number of threads can never decrease.
*
* This count refers to the number of threads that will be created lazily by the kernel, in
- * addition to the threads created by ABinderProcess_startThreadPool or
- * ABinderProcess_joinThreadPool.
+ * addition to the single threads created by ABinderProcess_startThreadPool (+1) or
+ * ABinderProcess_joinThreadPool (+1). Note: ABinderProcess_startThreadPool starts a thread
+ * itself, but it also enables up to the number of threads passed to this function to start.
+ * This function does not start any threads itself; it only configures
+ * ABinderProcess_startThreadPool.
*
* Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
* function should be responsible for configuring the threadpool for the entire application.
@@ -63,8 +66,8 @@
bool ABinderProcess_isThreadPoolStarted(void);
/**
* This adds the current thread to the threadpool. This thread will be in addition to the thread
- * started by ABinderProcess_startThreadPool and the lazy kernel-started threads specified by
- * ABinderProcess_setThreadPoolMaxThreadCount.
+ * configured with ABinderProcess_setThreadPoolMaxThreadCount and started with
+ * ABinderProcess_startThreadPool.
*
* Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
* function should be responsible for configuring the threadpool for the entire application.
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 2e46345..174fe8a 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -32,6 +32,7 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.microfuchsia",
"com.android.uwb",
"com.android.virt",
],
@@ -60,6 +61,7 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.microfuchsia",
"com.android.uwb",
"com.android.virt",
],
@@ -93,6 +95,7 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.microfuchsia",
"com.android.uwb",
"com.android.virt",
],
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 2cec243..c6fd487 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -301,7 +301,8 @@
auto proc = createRpcTestSocketServerProcess({});
- sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+ sp<IBinder> someRealBinder = defaultServiceManager()->getService(String16("activity"));
+ ASSERT_NE(someRealBinder, nullptr);
sp<IBinder> outBinder;
EXPECT_EQ(INVALID_OPERATION,
proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index 17d4d87..9c48b49 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -37,23 +37,23 @@
/// BufferPublishers are required to adhere to the following, based on the
/// reactive streams specification:
/// * The total number of on_next´s signalled by a Publisher to a Subscriber
-/// MUST be less than or equal to the total number of elements requested by that
-/// Subscriber´s Subscription at all times.
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
/// * A Publisher MAY signal fewer on_next than requested and terminate the
-/// Subscription by calling on_complete or on_error.
+/// Subscription by calling on_complete or on_error.
/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
-/// MUST be signaled serially.
+/// MUST be signaled serially.
/// * If a Publisher fails it MUST signal an on_error.
/// * If a Publisher terminates successfully (finite stream) it MUST signal an
-/// on_complete.
+/// on_complete.
/// * If a Publisher signals either on_error or on_complete on a Subscriber,
-/// that Subscriber’s Subscription MUST be considered cancelled.
+/// that Subscriber’s Subscription MUST be considered cancelled.
/// * Once a terminal state has been signaled (on_error, on_complete) it is
-/// REQUIRED that no further signals occur.
+/// REQUIRED that no further signals occur.
/// * If a Subscription is cancelled its Subscriber MUST eventually stop being
-/// signaled.
+/// signaled.
/// * A Publisher MAY support multiple Subscribers and decides whether each
-/// Subscription is unicast or multicast.
+/// Subscription is unicast or multicast.
pub trait BufferPublisher {
/// Returns the StreamConfig of buffers that publisher creates.
fn get_publisher_stream_config(&self) -> StreamConfig;
@@ -69,25 +69,25 @@
/// BufferSubcribers are required to adhere to the following, based on the
/// reactive streams specification:
/// * The total number of on_next´s signalled by a Publisher to a Subscriber
-/// MUST be less than or equal to the total number of elements requested by that
-/// Subscriber´s Subscription at all times.
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
/// * A Publisher MAY signal fewer on_next than requested and terminate the
-/// Subscription by calling on_complete or on_error.
+/// Subscription by calling on_complete or on_error.
/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
-/// MUST be signaled serially.
+/// MUST be signaled serially.
/// * If a Publisher fails it MUST signal an on_error.
/// * If a Publisher terminates successfully (finite stream) it MUST signal an
-/// on_complete.
+/// on_complete.
/// * If a Publisher signals either on_error or on_complete on a Subscriber,
-/// that Subscriber’s Subscription MUST be considered cancelled.
+/// that Subscriber’s Subscription MUST be considered cancelled.
/// * Once a terminal state has been signaled (on_error, on_complete) it is
-/// REQUIRED that no further signals occur.
+/// REQUIRED that no further signals occur.
/// * If a Subscription is cancelled its Subscriber MUST eventually stop being
-/// signaled.
+/// signaled.
/// * Publisher.subscribe MAY be called as many times as wanted but MUST be
-/// with a different Subscriber each time.
+/// with a different Subscriber each time.
/// * A Publisher MAY support multiple Subscribers and decides whether each
-/// Subscription is unicast or multicast.
+/// Subscription is unicast or multicast.
pub trait BufferSubscriber {
/// The StreamConfig of buffers that this subscriber expects.
fn get_subscriber_stream_config(&self) -> StreamConfig;
@@ -111,39 +111,39 @@
/// BufferSubcriptions are required to adhere to the following, based on the
/// reactive streams specification:
/// * Subscription.request and Subscription.cancel MUST only be called inside
-/// of its Subscriber context.
+/// of its Subscriber context.
/// * The Subscription MUST allow the Subscriber to call Subscription.request
-/// synchronously from within on_next or on_subscribe.
+/// synchronously from within on_next or on_subscribe.
/// * Subscription.request MUST place an upper bound on possible synchronous
-/// recursion between Publisher and Subscriber.
+/// recursion between Publisher and Subscriber.
/// * Subscription.request SHOULD respect the responsivity of its caller by
-/// returning in a timely manner.
+/// returning in a timely manner.
/// * Subscription.cancel MUST respect the responsivity of its caller by
-/// returning in a timely manner, MUST be idempotent and MUST be thread-safe.
+/// returning in a timely manner, MUST be idempotent and MUST be thread-safe.
/// * After the Subscription is cancelled, additional
-/// Subscription.request(n: u64) MUST be NOPs.
+/// Subscription.request(n: u64) MUST be NOPs.
/// * After the Subscription is cancelled, additional Subscription.cancel()
-/// MUST be NOPs.
+/// MUST be NOPs.
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MUST register the given number of additional elements to be produced to the
-/// respective subscriber.
+/// MUST register the given number of additional elements to be produced to the
+/// respective subscriber.
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MUST signal on_error if the argument is <= 0. The cause message SHOULD
-/// explain that non-positive request signals are illegal.
+/// MUST signal on_error if the argument is <= 0. The cause message SHOULD
+/// explain that non-positive request signals are illegal.
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MAY synchronously call on_next on this (or other) subscriber(s).
+/// MAY synchronously call on_next on this (or other) subscriber(s).
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MAY synchronously call on_complete or on_error on this (or other)
-/// subscriber(s).
+/// MAY synchronously call on_complete or on_error on this (or other)
+/// subscriber(s).
/// * While the Subscription is not cancelled, Subscription.cancel() MUST
-/// request the Publisher to eventually stop signaling its Subscriber. The
-/// operation is NOT REQUIRED to affect the Subscription immediately.
+/// request the Publisher to eventually stop signaling its Subscriber. The
+/// operation is NOT REQUIRED to affect the Subscription immediately.
/// * While the Subscription is not cancelled, Subscription.cancel() MUST
-/// request the Publisher to eventually drop any references to the corresponding
-/// subscriber.
+/// request the Publisher to eventually drop any references to the corresponding
+/// subscriber.
/// * While the Subscription is not cancelled, calling Subscription.cancel MAY
-/// cause the Publisher, if stateful, to transition into the shut-down state if
-/// no other Subscription exists at this point.
+/// cause the Publisher, if stateful, to transition into the shut-down state if
+/// no other Subscription exists at this point.
/// * Calling Subscription.cancel MUST return normally.
/// * Calling Subscription.request MUST return normally.
pub trait BufferSubscription: Send + Sync + 'static {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 0714fd9..b109969 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -892,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/Surface.cpp b/libs/gui/Surface.cpp
index 4a8df9e..da3886c 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -43,7 +43,7 @@
#include <ui/GraphicBuffer.h>
#include <ui/Region.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItem.h>
#include <gui/ISurfaceComposer.h>
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b5d9366..7d3e5c1 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -43,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>
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..2bbed2b 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -16,4 +16,58 @@
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;
+}
+
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/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/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/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/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 1ecc216..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;
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/LayerState.h b/libs/gui/include/gui/LayerState.h
index d419945..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>
@@ -35,9 +37,7 @@
#include <ftl/flags.h>
#include <gui/BufferReleaseChannel.h>
-#include <gui/DisplayCaptureArgs.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
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/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 6852589..eb2a61d 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>
@@ -229,7 +229,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();
}
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/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 4232443..ab09dfc 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -28,7 +28,7 @@
#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/BufferQueue.h>
#include <gui/CpuConsumer.h>
diff --git a/libs/gui/tests/TestServer_test.cpp b/libs/gui/tests/TestServer_test.cpp
index 8712988..d640782 100644
--- a/libs/gui/tests/TestServer_test.cpp
+++ b/libs/gui/tests/TestServer_test.cpp
@@ -24,7 +24,7 @@
#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/BufferQueue.h>
#include <gui/CpuConsumer.h>
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 342f7f5..c663649 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <ftl/enum.h>
#include <input/Resampler.h>
#include <utils/Timers.h>
@@ -56,6 +57,11 @@
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);
}
@@ -73,21 +79,70 @@
void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
const size_t numSamples = motionEvent.getHistorySize() + 1;
- for (size_t i = 0; i < numSamples; ++i) {
+ 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{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
- Pointer{*motionEvent.getPointerProperties(0),
- motionEvent.getSamplePointerCoords()[i]}});
+ Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
}
}
-bool LegacyResampler::canInterpolate(const InputMessage& futureSample) const {
+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 nanoseconds delta =
- static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
+ 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;
@@ -104,15 +159,20 @@
<< "Not resampled. mLatestSamples must not be empty to interpolate.";
const Sample& pastSample = *(mLatestSamples.end() - 1);
+
const nanoseconds delta =
- static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
+ nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
const float alpha =
std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
- const PointerCoords resampledCoords =
- calculateResampledCoords(pastSample.pointer.coords,
- futureSample.body.motion.pointers[0].coords, alpha);
- return Sample{resampleTime, Pointer{pastSample.pointer.properties, resampledCoords}};
+ 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 {
@@ -124,6 +184,10 @@
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.";
@@ -160,16 +224,21 @@
const float alpha =
std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
delta;
- const PointerCoords resampledCoords =
- calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
- alpha);
- return Sample{newResampleTime, Pointer{presentSample.pointer.properties, resampledCoords}};
+ 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.pointer.coords, motionEvent.getId());
+ motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(),
+ motionEvent.getId());
}
void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent,
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
index 135f8b4..7ae9a28 100644
--- a/libs/input/tests/Resampler_test.cpp
+++ b/libs/input/tests/Resampler_test.cpp
@@ -59,6 +59,9 @@
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.
@@ -67,21 +70,18 @@
};
InputSample::operator InputMessage() const {
- InputMessage message;
- message.header.type = InputMessage::Type::MOTION;
- message.body.motion.pointerCount = pointers.size();
- message.body.motion.eventTime = static_cast<std::chrono::nanoseconds>(eventTime).count();
- message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
- message.body.motion.downTime = 0;
- const uint32_t pointerCount = message.body.motion.pointerCount;
- for (uint32_t i = 0; i < pointerCount; ++i) {
- message.body.motion.pointers[i].properties.id = pointers[i].id;
- message.body.motion.pointers[i].properties.toolType = pointers[i].toolType;
- message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
- message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
- message.body.motion.pointers[i].coords.isResampled = pointers[i].isResampled;
+ 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 message;
+ return messageBuilder.build();
}
struct InputStream {
@@ -132,14 +132,6 @@
std::unique_ptr<Resampler> mResampler;
- MotionEvent buildMotionEvent(const int32_t action, const nsecs_t eventTime,
- const std::vector<PointerBuilder>& pointers);
-
- InputMessage createMessage(const uint32_t pointerCount, const nsecs_t eventTime,
- const int32_t action,
- const std::vector<PointerProperties>& properties,
- const std::vector<PointerCoords>& coords);
-
/**
* Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample
* member function.
@@ -153,42 +145,14 @@
* 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 PointerCoords& expectedCoords);
+ void assertMotionEventIsResampledAndCoordsNear(
+ const MotionEvent& original, const MotionEvent& resampled,
+ const std::vector<PointerCoords>& expectedCoords);
void assertMotionEventIsNotResampled(const MotionEvent& original,
const MotionEvent& notResampled);
};
-MotionEvent ResamplerTest::buildMotionEvent(const int32_t action, const nsecs_t eventTime,
- const std::vector<PointerBuilder>& pointerBuilders) {
- MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
- .downTime(0)
- .eventTime(eventTime);
- for (const PointerBuilder& pointerBuilder : pointerBuilders) {
- motionEventBuilder.pointer(pointerBuilder);
- }
- return motionEventBuilder.build();
-}
-
-InputMessage ResamplerTest::createMessage(const uint32_t pointerCount, const nsecs_t eventTime,
- const int32_t action,
- const std::vector<PointerProperties>& properties,
- const std::vector<PointerCoords>& coords) {
- InputMessage message;
- message.header.type = InputMessage::Type::MOTION;
- message.body.motion.pointerCount = pointerCount;
- message.body.motion.eventTime = eventTime;
- message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
- message.body.motion.downTime = 0;
- for (uint32_t i = 0; i < pointerCount; ++i) {
- message.body.motion.pointers[i].properties = properties[i];
- message.body.motion.pointers[i].coords = coords[i];
- }
- return message;
-}
-
void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
const MotionEvent& afterCall) {
EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId());
@@ -207,18 +171,29 @@
EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId());
}
-void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original,
- const MotionEvent& resampled,
- const PointerCoords& expectedCoords) {
+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 PointerCoords& resampledCoords =
- resampled.getSamplePointerCoords()[resampled.getHistorySize()];
- EXPECT_TRUE(resampledCoords.isResampled);
- EXPECT_NEAR(expectedCoords.getX(), resampledCoords.getX(), EPSILON);
- EXPECT_NEAR(expectedCoords.getY(), resampledCoords.getY(), EPSILON);
+
+ 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,
@@ -233,7 +208,7 @@
constexpr float TOUCH_MAJOR_VALUE = 1.0f;
MotionEvent motionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
AMOTION_EVENT_ACTION_MOVE};
constexpr std::chrono::nanoseconds eventTime{10ms};
@@ -255,34 +230,40 @@
EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
- Pointer{.id = 0,
- .x = 2.2f,
- .y = 2.4f,
- .isResampled = true});
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.4f,
+ .isResampled = true}});
}
TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) {
MotionEvent motionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
- AMOTION_EVENT_ACTION_MOVE,
- .deviceId = 0};
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) {
MotionEvent motionFromFirstDevice =
- InputStream{{{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- {8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ 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{{{11ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ 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.
@@ -308,28 +289,30 @@
*/
TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
MotionEvent motionEvent =
- InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ 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 = 2.0f, .isResampled = false}}};
+ InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}};
const MotionEvent originalMotionEvent = motionEvent;
mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
- Pointer{.id = 0,
- .x = 1.2f,
- .y = 1.2f,
- .isResampled = true});
+ {Pointer{.id = 0,
+ .x = 1.2f,
+ .y = 2.4f,
+ .isResampled = true}});
}
TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) {
MotionEvent motionEvent =
- InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ 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 = 2.0f, .isResampled = false}}};
+ InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}};
const MotionEvent originalMotionEvent = motionEvent;
@@ -342,25 +325,26 @@
* Tests extrapolation given two MotionEvents with a single sample.
*/
TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) {
- MotionEvent previousMotionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
AMOTION_EVENT_ACTION_MOVE};
- mResampler->resampleMotionEvent(10ms, previousMotionEvent, nullptr);
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, nullptr);
- MotionEvent motionEvent =
- InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
AMOTION_EVENT_ACTION_MOVE};
- const MotionEvent originalMotionEvent = motionEvent;
+ const MotionEvent originalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, nullptr);
- assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
- Pointer{.id = 0,
- .x = 1.0f,
- .y = 1.0f,
- .isResampled = true});
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 4.4f,
+ .isResampled = true}});
// Integrity of the whole motionEvent
// History size should increment by 1
// Check if the resampled value is the last one
@@ -370,27 +354,30 @@
TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) {
MotionEvent motionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ 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 = 3.0f, .isResampled = false}}};
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 5.0f, .isResampled = false}}};
const MotionEvent originalMotionEvent = motionEvent;
mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
- Pointer{.id = 0,
- .x = 2.2f,
- .y = 2.2f,
- .isResampled = true});
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 3.4f,
+ .isResampled = true}});
}
TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) {
MotionEvent motionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ 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;
@@ -398,16 +385,17 @@
mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
- Pointer{.id = 0,
- .x = 2.2f,
- .y = 2.2f,
- .isResampled = true});
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 4.4f,
+ .isResampled = true}});
}
TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) {
MotionEvent motionEvent =
- InputStream{{{9ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ 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;
@@ -419,8 +407,9 @@
TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) {
MotionEvent motionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- {26ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ 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;
@@ -432,8 +421,9 @@
TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) {
MotionEvent motionEvent =
- InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- {25ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ 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;
@@ -441,9 +431,424 @@
mResampler->resampleMotionEvent(43ms, motionEvent, nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
- Pointer{.id = 0,
- .x = 2.4f,
- .y = 2.4f,
- .isResampled = true});
+ {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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(27ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, 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(11ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
} // namespace android
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 54afb71..ca13afc 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -167,7 +167,7 @@
}
}
- 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 322a2ac..f0760fd 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -57,7 +57,9 @@
return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f;
}
-bool isScaleMute() const { return mLevel == HapticLevel::MUTE || mScaleFactor == 0; }
+bool isScaleMute() const {
+ return mLevel == HapticLevel::MUTE || mScaleFactor == 0 || mAdaptiveScaleFactor == 0;
+}
std::string toString() const {
std::ostringstream os;
diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
index 7adc9c3..9369f80 100644
--- a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
+++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
@@ -188,9 +188,19 @@
}
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))) {
+ // 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 };
@@ -210,9 +220,23 @@
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 */);
@@ -234,9 +258,23 @@
EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
}
+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 */);
@@ -259,9 +297,24 @@
}
TEST_F_WITH_FLAGS(
+ 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 */);
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 625599a..397feda 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -511,8 +511,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";
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 76aa6eb..7bec94e 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.
@@ -367,6 +369,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;
@@ -458,6 +462,10 @@
*/
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;
+
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 2daf195..70f024e 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -725,6 +725,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 f0e53b5..a5b1249 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;
@@ -817,6 +818,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);
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 93785f6..021978d 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -141,6 +141,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) {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f60a8a..2cc0a00 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -104,6 +104,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;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index c44c48c..627df7f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -140,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 e5afcc7..75cc4bb 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -122,6 +122,8 @@
virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; }
virtual void updateLedState(bool reset) {}
+ virtual std::optional<HardwareProperties> getTouchpadHardwareProperties();
+
protected:
InputDeviceContext& mDeviceContext;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 588dc0c..dbc2872 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"
@@ -251,7 +252,8 @@
}
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
@@ -423,10 +425,8 @@
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
if (mTouchpadHardwareStateNotificationsEnabled) {
- // TODO(b/286551975): Notify policy of the touchpad hardware state.
- LOG(DEBUG) << "Notify touchpad hardware state here!";
+ getPolicy()->notifyTouchpadHardwareState(*state, rawEvent.deviceId);
}
-
updatePalmDetectionMetrics();
return sendHardwareState(rawEvent.when, rawEvent.readTime, *state);
} else {
@@ -499,4 +499,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 4856fe0..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;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 66d62f8..148ca5a 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -28,6 +28,7 @@
#include "accumulator/TouchButtonAccumulator.h"
#include "include/TouchpadHardwareState.h"
+#include "TouchpadHardwareState.h"
#include "include/gestures.h"
namespace android {
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 6099c91..d77d539 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -65,6 +65,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);
@@ -234,6 +245,13 @@
mDevicesChangedCondition.notify_all();
}
+void FakeInputReaderPolicy::notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ mTouchpadHardwareState = schs;
+ mTouchpadHardwareStateNotified.notify_all();
+}
+
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 94f1311..e5ba620 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,6 +83,8 @@
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+ void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
@@ -101,6 +104,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/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index fc8a7da..ea69fff 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -172,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/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index d552c19..3e4a19b 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);
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index fea0d9a..ddc3310 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -281,6 +281,8 @@
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 {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> layoutInfo) override {
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/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index a1a60e3..dbc458c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -595,6 +595,12 @@
return true;
}
+ if (FlagManager::getInstance().view_set_requested_frame_rate_mrr() &&
+ category == FrameRateCategory::NoPreference && vote.rate.isValid() &&
+ vote.type == FrameRateCompatibility::ExactOrMultiple) {
+ return true;
+ }
+
return false;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 566bb8e..be00079 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -127,7 +127,7 @@
mVsyncModulator->cancelRefreshRateChange();
mVsyncConfiguration->reset();
- updatePhaseConfiguration(pacesetterSelectorPtr()->getActiveMode().fps);
+ updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
@@ -487,7 +487,12 @@
}
}
-void Scheduler::updatePhaseConfiguration(Fps refreshRate) {
+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()),
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 88f0e94..1367ec3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -187,7 +187,7 @@
}
}
- void updatePhaseConfiguration(Fps);
+ void updatePhaseConfiguration(PhysicalDisplayId, Fps);
const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cc236ac..0c297d9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -65,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>
@@ -1280,20 +1280,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;
}
}
@@ -1340,19 +1334,13 @@
// 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) {
mScheduler->onDisplayModeChanged(displayId, mode);
@@ -1447,9 +1435,7 @@
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) {
mScheduler->onDisplayModeChanged(displayId, activeMode);
@@ -1473,11 +1459,9 @@
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() {
@@ -6993,7 +6977,8 @@
const sp<IScreenCaptureListener>& captureListener) {
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);
@@ -7006,7 +6991,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;
@@ -7032,7 +7017,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);
@@ -7045,17 +7030,21 @@
}
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, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7108,10 +7097,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, captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7124,20 +7114,23 @@
const sp<IScreenCaptureListener>& captureListener) {
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;
@@ -7154,26 +7147,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);
@@ -7199,8 +7193,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");
@@ -7209,14 +7204,15 @@
}
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, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index a56bb51..07c720f 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -119,6 +119,7 @@
DUMP_READ_ONLY_FLAG(connected_display);
DUMP_READ_ONLY_FLAG(enable_small_area_detection);
DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
+ DUMP_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr);
DUMP_READ_ONLY_FLAG(misc1);
DUMP_READ_ONLY_FLAG(vrr_config);
DUMP_READ_ONLY_FLAG(hotplug2);
@@ -222,6 +223,8 @@
FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
+FLAG_MANAGER_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr,
+ "debug.sf.view_set_requested_frame_rate_mrr")
FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 8799295..a4b4a2b 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -56,6 +56,7 @@
/// Trunk stable readonly flags ///
bool connected_display() const;
bool frame_rate_category_mrr() const;
+ bool view_set_requested_frame_rate_mrr() const;
bool enable_small_area_detection() const;
bool misc1() const;
bool vrr_config() const;
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 919ec17..886167e 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -126,6 +126,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"
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/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index d355e72..e6fed63 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -20,7 +20,7 @@
#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>
@@ -280,7 +280,7 @@
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);
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/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/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/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 52bb07a..7e84408 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -892,6 +892,50 @@
EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
}
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithFixedSourceAndNoPreferenceCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::view_set_requested_frame_rate_mrr, true);
+
+ 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);
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/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/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index c97f401..3d8124b 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -32,6 +32,8 @@
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;
@@ -114,6 +116,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()) {
@@ -313,6 +321,13 @@
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 cap = 0;
auto status = getHal()->getCapabilities(&cap);
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 20979bd..ae0d9ab 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -354,6 +354,8 @@
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)) {}
@@ -391,6 +393,9 @@
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.
const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
@@ -471,6 +476,9 @@
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<Effect>> getSupportedEffectsInternal() override final;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 7bcc59a..ba7e1f0 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -39,6 +39,7 @@
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;
@@ -681,3 +682,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 9a7c69d..83430d7 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -40,6 +40,7 @@
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;
@@ -369,3 +370,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/test_mocks.h b/services/vibratorservice/test/test_mocks.h
index 2f9451e..5e09084 100644
--- a/services/vibratorservice/test/test_mocks.h
+++ b/services/vibratorservice/test/test_mocks.h
@@ -41,6 +41,8 @@
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;
// -------------------------------------------------------------------------------------------------
@@ -89,6 +91,17 @@
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));