Merge "Revert "Adding ServiceManagerTestFuzzer to use seed corpus"" into main am: 5a16dd2e23 am: 20f1ef95f8 am: 932a16ddf7 am: d498f2cdc8 am: 4719aadde6
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/2687658
Change-Id: I67b87c3230d9d303abb41c9b24b3e578ef59a60d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index f6e4ec3..4081514 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -291,6 +291,8 @@
service = &(it->second);
if (!service->allowIsolated && is_multiuser_uid_isolated(ctx.uid)) {
+ LOG(WARNING) << "Isolated app with UID " << ctx.uid << " requested '" << name
+ << "', but the service is not allowed for isolated apps.";
return nullptr;
}
out = service->binder;
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
index d36c958..5966cba 100644
--- a/data/etc/android.hardware.telephony.satellite.xml
+++ b/data/etc/android.hardware.telephony.satellite.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<!-- Feature for devices that support satellite communication via satellite vendor service APIs. -->
+<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. -->
<permissions>
<feature name="android.hardware.telephony.satellite" />
</permissions>
diff --git a/include/android/input.h b/include/android/input.h
index 9a0eb4d..16d86af 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -54,16 +54,12 @@
#include <stdint.h>
#include <sys/types.h>
#include <android/keycodes.h>
-
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
#include <android/looper.h>
#include <jni.h>
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// definitions are provided.
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
diff --git a/include/android/looper.h b/include/android/looper.h
index 4fe142a..e50730d 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -26,10 +26,18 @@
#ifndef ANDROID_LOOPER_H
#define ANDROID_LOOPER_H
+#include <sys/cdefs.h>
+
#ifdef __cplusplus
extern "C" {
#endif
+// This file may also be built on glibc or on Windows/MacOS libc's, so
+// deprecated definitions are provided.
+#if !defined(__REMOVED_IN)
+#define __REMOVED_IN(__api_level) __attribute__((__deprecated__))
+#endif
+
struct ALooper;
/**
* ALooper
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index b494f89..cedd361 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -171,6 +171,7 @@
* @return 0 on success.
* EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group.
* EPIPE if communication with the system service has failed.
+ * EPERM if any thread id doesn't belong to the application.
*/
int APerformanceHint_setThreads(
APerformanceHintSession* session,
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 16c5dde..a618393 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -29,6 +29,8 @@
#ifndef ANDROID_SENSOR_H
#define ANDROID_SENSOR_H
+#include <sys/cdefs.h>
+
/******************************************************************
*
* IMPORTANT NOTICE:
@@ -45,11 +47,6 @@
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
-// This file is included by modules that have host support but android/looper.h is not supported
-// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
-#ifndef __BIONIC__
-#define __REMOVED_IN(x) __attribute__((deprecated))
-#endif
#include <android/looper.h>
#include <stdbool.h>
@@ -57,6 +54,8 @@
#include <math.h>
#include <stdint.h>
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// and deprecated definitions are provided.
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
@@ -658,7 +657,7 @@
uint32_t flags;
int32_t reserved1[3];
} ASensorEvent;
-// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
+// LINT.ThenChange(hardware/libhardware/include/hardware/sensors.h)
struct ASensorManager;
/**
diff --git a/include/input/Input.h b/include/input/Input.h
index 527a477..64ee473 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -282,6 +282,7 @@
// Indicates that the key represents a special gesture that has been detected by
// the touch firmware or driver. Causes touch events from the same device to be canceled.
+ // This policy flag prevents key events from changing touch mode state.
POLICY_FLAG_GESTURE = 0x00000008,
POLICY_FLAG_RAW_MASK = 0x0000ffff,
@@ -492,14 +493,17 @@
toolType = ToolType::UNKNOWN;
}
- bool operator==(const PointerProperties& other) const;
+ bool operator==(const PointerProperties& other) const = default;
inline bool operator!=(const PointerProperties& other) const {
return !(*this == other);
}
- void copyFrom(const PointerProperties& other);
+ PointerProperties& operator=(const PointerProperties&) = default;
};
+// TODO(b/211379801) : Use a strong type from ftl/mixins.h instead
+using DeviceId = int32_t;
+
/*
* Input events.
*/
@@ -511,7 +515,7 @@
inline int32_t getId() const { return mId; }
- inline int32_t getDeviceId() const { return mDeviceId; }
+ inline DeviceId getDeviceId() const { return mDeviceId; }
inline uint32_t getSource() const { return mSource; }
@@ -526,13 +530,13 @@
static int32_t nextId();
protected:
- void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac);
void initialize(const InputEvent& from);
int32_t mId;
- int32_t mDeviceId;
+ DeviceId mDeviceId;
uint32_t mSource;
int32_t mDisplayId;
std::array<uint8_t, 32> mHmac;
@@ -547,7 +551,7 @@
public:
virtual ~KeyEvent() { }
- virtual InputEventType getType() const { return InputEventType::KEY; }
+ InputEventType getType() const override { return InputEventType::KEY; }
inline int32_t getAction() const { return mAction; }
@@ -570,7 +574,7 @@
static const char* getLabel(int32_t keyCode);
static std::optional<int> getKeyCodeFromLabel(const char* label);
- void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime);
@@ -598,7 +602,7 @@
public:
virtual ~MotionEvent() { }
- virtual InputEventType getType() const { return InputEventType::MOTION; }
+ InputEventType getType() const override { return InputEventType::MOTION; }
inline int32_t getAction() const { return mAction; }
@@ -834,7 +838,7 @@
ssize_t findPointerIndex(int32_t pointerId) const;
- void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
@@ -926,7 +930,7 @@
public:
virtual ~FocusEvent() {}
- virtual InputEventType getType() const override { return InputEventType::FOCUS; }
+ InputEventType getType() const override { return InputEventType::FOCUS; }
inline bool getHasFocus() const { return mHasFocus; }
@@ -945,7 +949,7 @@
public:
virtual ~CaptureEvent() {}
- virtual InputEventType getType() const override { return InputEventType::CAPTURE; }
+ InputEventType getType() const override { return InputEventType::CAPTURE; }
inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; }
@@ -964,7 +968,7 @@
public:
virtual ~DragEvent() {}
- virtual InputEventType getType() const override { return InputEventType::DRAG; }
+ InputEventType getType() const override { return InputEventType::DRAG; }
inline bool isExiting() const { return mIsExiting; }
@@ -988,7 +992,7 @@
public:
virtual ~TouchModeEvent() {}
- virtual InputEventType getType() const override { return InputEventType::TOUCH_MODE; }
+ InputEventType getType() const override { return InputEventType::TOUCH_MODE; }
inline bool isInTouchMode() const { return mIsInTouchMode; }
@@ -1012,7 +1016,7 @@
};
Type type;
- int32_t deviceId;
+ DeviceId deviceId;
nsecs_t eventTimeNanos;
uint32_t source;
int32_t displayId;
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 909bf08..44247c1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -69,6 +69,12 @@
static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value);
+ static std::optional<int> getLinuxEvdevEventTypeByLabel(const char* label);
+
+ static std::optional<int> getLinuxEvdevEventCodeByLabel(int32_t type, const char* label);
+
+ static std::optional<int> getLinuxEvdevInputPropByLabel(const char* label);
+
private:
InputEventLookup();
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index 3715408..b857482 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -51,6 +51,8 @@
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, int32_t flags);
+ void resetDevice(int32_t deviceId);
+
private:
rust::Box<android::input::verifier::InputVerifier> mVerifier;
};
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 8c3c74a..b126abe 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,13 +17,13 @@
#pragma once
#include <android-base/result.h>
+#include <input/InputDevice.h>
+
#include <stdint.h>
#include <utils/Errors.h>
#include <utils/Tokenizer.h>
#include <set>
-#include <input/InputDevice.h>
-
namespace android {
struct AxisInfo {
diff --git a/include/input/RingBuffer.h b/include/input/RingBuffer.h
index 37fe5af..d2747d6 100644
--- a/include/input/RingBuffer.h
+++ b/include/input/RingBuffer.h
@@ -24,7 +24,6 @@
#include <type_traits>
#include <utility>
-#include <android-base/logging.h>
#include <android-base/stringprintf.h>
namespace android {
@@ -277,15 +276,16 @@
// Converts the index of an element in [0, size()] to its corresponding index in mBuffer.
size_type bufferIndex(size_type elementIndex) const {
- CHECK_LE(elementIndex, size());
+ if (elementIndex > size()) {
+ abort();
+ }
size_type index = mBegin + elementIndex;
if (index >= capacity()) {
index -= capacity();
}
- CHECK_LT(index, capacity())
- << android::base::StringPrintf("Invalid index calculated for element (%zu) "
- "in buffer of size %zu",
- elementIndex, size());
+ if (index >= capacity()) {
+ abort();
+ }
return index;
}
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index f3c201e..b78f63e 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -88,7 +88,7 @@
VelocityControl();
/* Gets the various parameters. */
- VelocityControlParameters& getParameters();
+ const VelocityControlParameters& getParameters() const;
/* Sets the various parameters. */
void setParameters(const VelocityControlParameters& parameters);
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index da97c3e..b58feac 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -17,6 +17,7 @@
#pragma once
#include <input/Input.h>
+#include <input/RingBuffer.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
#include <map>
@@ -31,6 +32,8 @@
*/
class VelocityTracker {
public:
+ static const size_t MAX_DEGREE = 4;
+
enum class Strategy : int32_t {
DEFAULT = -1,
MIN = 0,
@@ -47,23 +50,6 @@
MAX = LEGACY,
};
- struct Estimator {
- static const size_t MAX_DEGREE = 4;
-
- // Estimator time base.
- nsecs_t time = 0;
-
- // Polynomial coefficients describing motion.
- std::array<float, MAX_DEGREE + 1> coeff{};
-
- // Polynomial degree (number of coefficients), or zero if no information is
- // available.
- uint32_t degree = 0;
-
- // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
- float confidence = 0;
- };
-
/*
* Contains all available velocity data from a VelocityTracker.
*/
@@ -124,11 +110,6 @@
// [-maxVelocity, maxVelocity], inclusive.
ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity);
- // Gets an estimator for the recent movements of the specified pointer id for the given axis.
- // Returns false and clears the estimator if there is no information available
- // about the pointer.
- std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const;
-
// Gets the active pointer id, or -1 if none.
inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); }
@@ -169,14 +150,48 @@
virtual void clearPointer(int32_t pointerId) = 0;
virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0;
- virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0;
+ virtual std::optional<float> getVelocity(int32_t pointerId) const = 0;
};
+/**
+ * A `VelocityTrackerStrategy` that accumulates added data points and processes the accumulated data
+ * points when getting velocity.
+ */
+class AccumulatingVelocityTrackerStrategy : public VelocityTrackerStrategy {
+public:
+ AccumulatingVelocityTrackerStrategy(nsecs_t horizonNanos, bool maintainHorizonDuringAdd);
+
+ void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
+ void clearPointer(int32_t pointerId) override;
+
+protected:
+ struct Movement {
+ nsecs_t eventTime;
+ float position;
+ };
+
+ // Number of samples to keep.
+ // If different strategies would like to maintain different history size, we can make this a
+ // protected const field.
+ static constexpr uint32_t HISTORY_SIZE = 20;
+
+ /**
+ * Duration, in nanoseconds, since the latest movement where a movement may be considered for
+ * velocity calculation.
+ */
+ const nsecs_t mHorizonNanos;
+ /**
+ * If true, data points outside of horizon (see `mHorizonNanos`) will be cleared after each
+ * addition of a new movement.
+ */
+ const bool mMaintainHorizonDuringAdd;
+ std::map<int32_t /*pointerId*/, RingBuffer<Movement>> mMovements;
+};
/*
* Velocity tracker algorithm based on least-squares linear regression.
*/
-class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class LeastSquaresVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
public:
enum class Weighting {
// No weights applied. All data points are equally reliable.
@@ -193,13 +208,11 @@
RECENT,
};
- // Degree must be no greater than Estimator::MAX_DEGREE.
+ // Degree must be no greater than VelocityTracker::MAX_DEGREE.
LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE);
~LeastSquaresVelocityTrackerStrategy() override;
- void clearPointer(int32_t pointerId) override;
- void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
- std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+ std::optional<float> getVelocity(int32_t pointerId) const override;
private:
// Sample horizon.
@@ -207,23 +220,19 @@
// changes in direction.
static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
- // Number of samples to keep.
- static const uint32_t HISTORY_SIZE = 20;
-
- struct Movement {
- nsecs_t eventTime;
- float position;
- };
-
float chooseWeight(int32_t pointerId, uint32_t index) const;
+ /**
+ * An optimized least-squares solver for degree 2 and no weight (i.e. `Weighting.NONE`).
+ * The provided container of movements shall NOT be empty, and shall have the movements in
+ * chronological order.
+ */
+ std::optional<float> solveUnweightedLeastSquaresDeg2(
+ const RingBuffer<Movement>& movements) const;
const uint32_t mDegree;
const Weighting mWeighting;
- std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
- std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
};
-
/*
* Velocity tracker algorithm that uses an IIR filter.
*/
@@ -235,7 +244,7 @@
void clearPointer(int32_t pointerId) override;
void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override;
- std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+ std::optional<float> getVelocity(int32_t pointerId) const override;
private:
// Current state estimate for a particular pointer.
@@ -252,49 +261,33 @@
void initState(State& state, nsecs_t eventTime, float pos) const;
void updateState(State& state, nsecs_t eventTime, float pos) const;
- void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
};
/*
* Velocity tracker strategy used prior to ICS.
*/
-class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class LegacyVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
public:
LegacyVelocityTrackerStrategy();
~LegacyVelocityTrackerStrategy() override;
- void clearPointer(int32_t pointerId) override;
- void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
- std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+ std::optional<float> getVelocity(int32_t pointerId) const override;
private:
// Oldest sample to consider when calculating the velocity.
static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
- // Number of samples to keep.
- static const uint32_t HISTORY_SIZE = 20;
-
// The minimum duration between samples when estimating velocity.
static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
-
- struct Movement {
- nsecs_t eventTime;
- float position;
- };
-
- std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
- std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
};
-class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
+class ImpulseVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy {
public:
ImpulseVelocityTrackerStrategy(bool deltaValues);
~ImpulseVelocityTrackerStrategy() override;
- void clearPointer(int32_t pointerId) override;
- void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
- std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
+ std::optional<float> getVelocity(int32_t pointerId) const override;
private:
// Sample horizon.
@@ -302,21 +295,10 @@
// changes in direction.
static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms
- // Number of samples to keep.
- static constexpr size_t HISTORY_SIZE = 20;
-
- struct Movement {
- nsecs_t eventTime;
- float position;
- };
-
// Whether or not the input movement values for the strategy come in the form of delta values.
// If the input values are not deltas, the strategy needs to calculate deltas as part of its
// velocity calculation.
const bool mDeltaValues;
-
- std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
- std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
};
} // namespace android
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 82e4551..9e426d3 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -17,11 +17,11 @@
#ifndef ANDROID_POWERHALCONTROLLER_H
#define ANDROID_POWERHALCONTROLLER_H
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalWrapper.h>
namespace android {
@@ -55,11 +55,13 @@
virtual void init();
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) override;
+ virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
+ bool enabled) override;
+ virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+ createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
private:
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index e0384f3..cbbfa59 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -17,11 +17,11 @@
#ifndef ANDROID_POWERHALLOADER_H
#define ANDROID_POWERHALLOADER_H
+#include <aidl/android/hardware/power/IPower.h>
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
namespace android {
@@ -31,7 +31,7 @@
class PowerHalLoader {
public:
static void unloadAll();
- static sp<hardware::power::IPower> loadAidl();
+ static std::shared_ptr<aidl::android::hardware::power::IPower> loadAidl();
static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
@@ -39,7 +39,7 @@
private:
static std::mutex gHalMutex;
- static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+ static std::shared_ptr<aidl::android::hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex);
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 8028aa8..4e4a1b0 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -17,14 +17,15 @@
#ifndef ANDROID_POWERHALWRAPPER_H
#define ANDROID_POWERHALWRAPPER_H
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <binder/Status.h>
namespace android {
@@ -47,7 +48,7 @@
}
static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
- static HalResult<T> fromStatus(binder::Status status, T data) {
+ static HalResult<T> fromStatus(const binder::Status& status, T data) {
if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
return HalResult<T>::unsupported();
}
@@ -56,14 +57,28 @@
}
return HalResult<T>::failed(std::string(status.toString8().c_str()));
}
- static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data);
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) {
+ if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed(std::string(status.getDescription()));
+ }
template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+ }
template <typename R>
static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T data);
+ T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
+ }
// This will throw std::bad_optional_access if this result is not ok.
const T& value() const { return mValue.value(); }
@@ -91,12 +106,30 @@
static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
- static HalResult<void> fromStatus(status_t status);
- static HalResult<void> fromStatus(binder::Status status);
- static HalResult<void> fromStatus(hardware::power::V1_0::Status status);
+ static HalResult<void> fromStatus(const binder::Status& status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
+ if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.getDescription()));
+ }
template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>& ret);
+ static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+ }
bool isOk() const { return !mUnsupported && !mFailed; }
bool isFailed() const { return !mUnsupported && mFailed; }
@@ -119,11 +152,12 @@
public:
virtual ~HalWrapper() = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) = 0;
+ virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) = 0;
+ virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
+ virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+ createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
};
@@ -131,14 +165,15 @@
class EmptyHalWrapper : public HalWrapper {
public:
EmptyHalWrapper() = default;
- ~EmptyHalWrapper() = default;
+ ~EmptyHalWrapper() override = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+ HalResult<int64_t> getHintSessionPreferredRate() override;
};
// Wrapper for the HIDL Power HAL v1.0.
@@ -146,14 +181,15 @@
public:
explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0)
: mHandleV1_0(std::move(handleV1_0)) {}
- virtual ~HidlHalWrapperV1_0() = default;
+ ~HidlHalWrapperV1_0() override = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+ HalResult<int64_t> getHintSessionPreferredRate() override;
protected:
const sp<hardware::power::V1_0::IPower> mHandleV1_0;
@@ -167,67 +203,71 @@
// Wrapper for the HIDL Power HAL v1.1.
class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
public:
- HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
+ explicit HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
: HidlHalWrapperV1_0(std::move(handleV1_1)) {}
- virtual ~HidlHalWrapperV1_1() = default;
+ ~HidlHalWrapperV1_1() override = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
- uint32_t data) override;
+ HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
};
// Wrapper for the HIDL Power HAL v1.2.
class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
public:
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
: HidlHalWrapperV1_1(std::move(handleV1_2)) {}
- virtual ~HidlHalWrapperV1_2() = default;
+ ~HidlHalWrapperV1_2() override = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
- uint32_t data) override;
+ HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
};
// Wrapper for the HIDL Power HAL v1.3.
class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
public:
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
: HidlHalWrapperV1_2(std::move(handleV1_3)) {}
- virtual ~HidlHalWrapperV1_3() = default;
+ ~HidlHalWrapperV1_3() override = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
- uint32_t data) override;
+ HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data) override;
};
// Wrapper for the AIDL Power HAL.
class AidlHalWrapper : public HalWrapper {
public:
- explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
- virtual ~AidlHalWrapper() = default;
+ explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle)
+ : mHandle(std::move(handle)) {}
+ ~AidlHalWrapper() override = default;
- virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
- virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
- virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) override;
+ HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
+ HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- virtual HalResult<int64_t> getHintSessionPreferredRate() override;
+ HalResult<int64_t> getHintSessionPreferredRate() override;
private:
// Control access to the boost and mode supported arrays.
std::mutex mBoostMutex;
std::mutex mModeMutex;
- sp<hardware::power::IPower> mHandle;
+ std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
// Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
// Need to increase the array size if more boost supported.
- std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ std::array<
+ std::atomic<HalSupport>,
+ static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
+ 1>
mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(*(android::enum_range<hardware::power::Mode>().end() - 1)) + 1>
+ static_cast<int32_t>(
+ *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
+ 1>
mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
};
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index aca5009..5264276 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -21,6 +21,7 @@
#include <binder/ActivityManager.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <utils/SystemClock.h>
@@ -33,27 +34,36 @@
sp<IActivityManager> ActivityManager::getService()
{
std::lock_guard<Mutex> scoped_lock(mLock);
- int64_t startTime = 0;
sp<IActivityManager> service = mService;
- while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
- sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
- if (binder == nullptr) {
- // Wait for the activity service to come back...
- if (startTime == 0) {
- startTime = uptimeMillis();
- ALOGI("Waiting for activity service");
- } else if ((uptimeMillis() - startTime) > 1000000) {
- ALOGW("Waiting too long for activity service, giving up");
- service = nullptr;
- break;
- }
- usleep(25000);
- } else {
+ if (ProcessState::self()->isThreadPoolStarted()) {
+ if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->waitForService(String16("activity"));
service = interface_cast<IActivityManager>(binder);
mService = service;
}
+ } else {
+ ALOGI("Thread pool not started. Polling for activity service.");
+ int64_t startTime = 0;
+ while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity"));
+ if (binder == nullptr) {
+ // Wait for the activity service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGI("Waiting for activity service");
+ } else if ((uptimeMillis() - startTime) > 1000000) {
+ ALOGW("Waiting too long for activity service, giving up");
+ service = nullptr;
+ break;
+ }
+ usleep(25000);
+ } else {
+ service = interface_cast<IActivityManager>(binder);
+ mService = service;
+ }
+ }
}
- return service;
+ return mService;
}
int ActivityManager::openContentUri(const String16& stringUri)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9b685f9..bbaa419 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -69,6 +69,10 @@
typedef uintptr_t binder_uintptr_t;
#endif // BINDER_WITH_KERNEL_IPC
+#ifdef __BIONIC__
+#include <android/fdsan.h>
+#endif
+
#define LOG_REFS(...)
// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOG_ALLOC(...)
@@ -109,6 +113,37 @@
// Maximum size of a blob to transfer in-place.
static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
+#if defined(__BIONIC__)
+static void FdTag(int fd, const void* old_addr, const void* new_addr) {
+ if (android_fdsan_exchange_owner_tag) {
+ uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+ reinterpret_cast<uint64_t>(old_addr));
+ uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+ reinterpret_cast<uint64_t>(new_addr));
+ android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+ }
+}
+static void FdTagClose(int fd, const void* addr) {
+ if (android_fdsan_close_with_tag) {
+ uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_PARCEL,
+ reinterpret_cast<uint64_t>(addr));
+ android_fdsan_close_with_tag(fd, tag);
+ } else {
+ close(fd);
+ }
+}
+#else
+static void FdTag(int fd, const void* old_addr, const void* new_addr) {
+ (void)fd;
+ (void)old_addr;
+ (void)new_addr;
+}
+static void FdTagClose(int fd, const void* addr) {
+ (void)addr;
+ close(fd);
+}
+#endif
+
enum {
BLOB_INPLACE = 0,
BLOB_ASHMEM_IMMUTABLE = 1,
@@ -134,6 +169,9 @@
return;
}
case BINDER_TYPE_FD: {
+ if (obj.cookie != 0) { // owned
+ FdTag(obj.handle, nullptr, who);
+ }
return;
}
}
@@ -159,8 +197,10 @@
return;
}
case BINDER_TYPE_FD: {
+ // note: this path is not used when mOwner, so the tag is also released
+ // in 'closeFileDescriptors'
if (obj.cookie != 0) { // owned
- close(obj.handle);
+ FdTagClose(obj.handle, who);
}
return;
}
@@ -554,7 +594,6 @@
kernelFields->mObjectsSize++;
flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off);
- acquire_object(proc, *flat, this);
if (flat->hdr.type == BINDER_TYPE_FD) {
// If this is a file descriptor, we need to dup it so the
@@ -567,6 +606,8 @@
err = FDS_NOT_ALLOWED;
}
}
+
+ acquire_object(proc, *flat, this);
}
}
#else
@@ -2553,7 +2594,8 @@
reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
if (flat->hdr.type == BINDER_TYPE_FD) {
// ALOGI("Closing fd: %ld", flat->handle);
- close(flat->handle);
+ // FDs from the kernel are always owned
+ FdTagClose(flat->handle, this);
}
}
#else // BINDER_WITH_KERNEL_IPC
@@ -2634,6 +2676,10 @@
kernelFields->mObjectsSize = 0;
break;
}
+ if (type == BINDER_TYPE_FD) {
+ // FDs from the kernel are always owned
+ FdTag(flat->handle, 0, this);
+ }
minOffset = offset + sizeof(flat_binder_object);
}
scanForFds();
diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp
new file mode 100644
index 0000000..e1dc9ba
--- /dev/null
+++ b/libs/bufferstreams/Android.bp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
diff --git a/libs/bufferstreams/OWNERS b/libs/bufferstreams/OWNERS
new file mode 100644
index 0000000..32b72b8
--- /dev/null
+++ b/libs/bufferstreams/OWNERS
@@ -0,0 +1,7 @@
+carlosmr@google.com
+hibrian@google.com
+jreck@google.com
+jshargo@google.com
+
+file:/services/surfaceflinger/OWNERS
+
diff --git a/libs/bufferstreams/README.md b/libs/bufferstreams/README.md
new file mode 100644
index 0000000..860adef
--- /dev/null
+++ b/libs/bufferstreams/README.md
@@ -0,0 +1,13 @@
+# libbufferstreams: Reactive Streams for Graphics Buffers
+
+This library is currently **experimental** and **under active development**.
+It is not production ready yet.
+
+For more information on reactive streams, please see <https://www.reactive-streams.org/>
+
+## Contributing
+
+This library is natively written in Rust and exposes a C API. If you make changes to the Rust API,
+you **must** update the C API in turn. To do so, with cbindgen installed, run:
+
+```$ ./update_include.sh```
diff --git a/libs/bufferstreams/include/bufferstreams.h b/libs/bufferstreams/include/bufferstreams.h
new file mode 100644
index 0000000..5308de2
--- /dev/null
+++ b/libs/bufferstreams/include/bufferstreams.h
@@ -0,0 +1,13 @@
+/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+
+/**
+ * This function will print Hello World.
+ */
+bool hello(void);
diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp
new file mode 100644
index 0000000..95a85b5
--- /dev/null
+++ b/libs/bufferstreams/rust/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_library {
+ name: "libbufferstreams",
+ crate_name: "bufferstreams",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ vendor_available: true,
+ host_supported: true,
+ min_sdk_version: "30",
+}
diff --git a/libs/bufferstreams/rust/Cargo.lock b/libs/bufferstreams/rust/Cargo.lock
new file mode 100644
index 0000000..4482dba
--- /dev/null
+++ b/libs/bufferstreams/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bufferstreams"
+version = "0.1.0"
diff --git a/libs/bufferstreams/rust/Cargo.toml b/libs/bufferstreams/rust/Cargo.toml
new file mode 100644
index 0000000..d30c55c
--- /dev/null
+++ b/libs/bufferstreams/rust/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "bufferstreams"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/libs/bufferstreams/rust/cbindgen.toml b/libs/bufferstreams/rust/cbindgen.toml
new file mode 100644
index 0000000..eda837f
--- /dev/null
+++ b/libs/bufferstreams/rust/cbindgen.toml
@@ -0,0 +1,149 @@
+# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
+# for detailed documentation of every option here.
+
+
+
+language = "C"
+
+
+
+############## Options for Wrapping the Contents of the Header #################
+
+# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
+# trailer = "/* Text to put at the end of the generated file */"
+# include_guard = "my_bindings_h"
+# pragma_once = true
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+include_version = false
+# namespace = "my_namespace"
+namespaces = []
+using_namespaces = []
+sys_includes = []
+includes = []
+no_includes = false
+after_includes = ""
+
+
+
+
+############################ Code Style Options ################################
+
+braces = "SameLine"
+line_length = 100
+tab_width = 2
+documentation = true
+documentation_style = "auto"
+documentation_length = "full"
+line_endings = "LF" # also "CR", "CRLF", "Native"
+
+
+
+
+############################# Codegen Options ##################################
+
+style = "both"
+sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
+usize_is_size_t = true
+
+
+
+[defines]
+# "target_os = freebsd" = "DEFINE_FREEBSD"
+# "feature = serde" = "DEFINE_SERDE"
+
+
+
+[export]
+include = []
+exclude = []
+# prefix = "CAPI_"
+item_types = []
+renaming_overrides_prefixing = false
+
+
+
+[export.rename]
+
+
+
+[export.body]
+
+
+[export.mangle]
+
+
+[fn]
+rename_args = "None"
+# must_use = "MUST_USE_FUNC"
+# no_return = "NO_RETURN"
+# prefix = "START_FUNC"
+# postfix = "END_FUNC"
+args = "auto"
+sort_by = "Name"
+
+
+
+
+[struct]
+rename_fields = "None"
+# must_use = "MUST_USE_STRUCT"
+derive_constructor = false
+derive_eq = false
+derive_neq = false
+derive_lt = false
+derive_lte = false
+derive_gt = false
+derive_gte = false
+
+
+
+
+[enum]
+rename_variants = "None"
+# must_use = "MUST_USE_ENUM"
+add_sentinel = false
+prefix_with_name = false
+derive_helper_methods = false
+derive_const_casts = false
+derive_mut_casts = false
+# cast_assert_name = "ASSERT"
+derive_tagged_enum_destructor = false
+derive_tagged_enum_copy_constructor = false
+enum_class = true
+private_default_tagged_enum_constructor = false
+
+
+
+
+[const]
+allow_static_const = true
+allow_constexpr = false
+sort_by = "Name"
+
+
+
+
+[macro_expansion]
+bitflags = false
+
+
+
+
+
+
+############## Options for How Your Rust library Should Be Parsed ##############
+
+[parse]
+parse_deps = false
+# include = []
+exclude = []
+clean = false
+extra_bindings = []
+
+
+
+[parse.expand]
+crates = []
+all_features = false
+default_features = true
+features = []
\ No newline at end of file
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
new file mode 100644
index 0000000..51f1c73
--- /dev/null
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! libbufferstreams: Reactive Streams for Graphics Buffers
+
+/// This function will print Hello World.
+#[no_mangle]
+pub extern "C" fn hello() -> bool {
+ println!("Hello world.");
+ true
+}
diff --git a/libs/bufferstreams/update_include.sh b/libs/bufferstreams/update_include.sh
new file mode 100755
index 0000000..e986e9f
--- /dev/null
+++ b/libs/bufferstreams/update_include.sh
@@ -0,0 +1,2 @@
+cd rust
+cbindgen --config cbindgen.toml --crate bufferstreams --output ../include/bufferstreams.h
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 732ca36..715822b 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -512,11 +512,7 @@
return mShouldUseAngle;
}
-// Set ANGLE information.
-// If path is "system", it means system ANGLE must be used for the process.
-// If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
-// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
-void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
+void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseSystemAngle,
const std::string& packageName,
const std::vector<std::string> eglFeatures) {
if (mShouldUseAngle) {
@@ -533,13 +529,8 @@
mAnglePath = std::move(path);
ALOGV("setting app package name to '%s'", packageName.c_str());
mPackageName = std::move(packageName);
- if (mAnglePath == "system") {
- mShouldUseSystemAngle = true;
- }
- if (!mAnglePath.empty()) {
- mShouldUseAngle = true;
- }
- mShouldUseNativeDriver = shouldUseNativeDriver;
+ mShouldUseAngle = true;
+ mShouldUseSystemAngle = shouldUseSystemAngle;
}
std::string& GraphicsEnv::getPackageName() {
@@ -616,10 +607,6 @@
return mShouldUseSystemAngle;
}
-bool GraphicsEnv::shouldUseNativeDriver() {
- return mShouldUseNativeDriver;
-}
-
/**
* APIs for debuggable layers
*/
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 4c070ae..1c0439e 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -180,9 +180,9 @@
return reply->writeUtf8AsUtf16(driverPath);
}
case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
+ int in = dup(data.readFileDescriptor());
+ int out = dup(data.readFileDescriptor());
+ int err = dup(data.readFileDescriptor());
std::vector<String16> args;
data.readString16Vector(&args);
@@ -195,6 +195,9 @@
status = shellCommand(in, out, err, args);
if (resultReceiver != nullptr) resultReceiver->send(status);
+ ::close(in);
+ ::close(out);
+ ::close(err);
return OK;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 6cce3f6..fbf2902 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -108,10 +108,7 @@
// (libraries must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
- // If the search patch is "system", then it means the system ANGLE should be used.
- // If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
- // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
- void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
+ void setAngleInfo(const std::string& path, const bool useSystemAngle,
const std::string& packageName, const std::vector<std::string> eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
@@ -121,7 +118,6 @@
// Set the persist.graphics.egl system property value.
void nativeToggleAngleAsSystemDriver(bool enabled);
bool shouldUseSystemAngle();
- bool shouldUseNativeDriver();
/*
* Apis for debug layer
@@ -179,8 +175,6 @@
bool mShouldUseAngle = false;
// Whether loader should load system ANGLE.
bool mShouldUseSystemAngle = false;
- // Whether loader should load native GLES driver.
- bool mShouldUseNativeDriver = false;
// ANGLE namespace.
android_namespace_t* mAngleNamespace = nullptr;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 808388f..b872541 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -418,6 +418,9 @@
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
+ sp<IConsumerListener> listener;
+ bool callOnFrameDequeued = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameDequeued == true
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
@@ -561,10 +564,11 @@
}
if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
- if (mCore->mConsumerListener != nullptr) {
- mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
- }
+ callOnFrameDequeued = true;
+ bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
+
+ listener = mCore->mConsumerListener;
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -581,10 +585,8 @@
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
- if (mCore->mConsumerListener != nullptr) {
- mCore->mConsumerListener->onFrameDequeued(
- mSlots[*outSlot].mGraphicBuffer->getId());
- }
+ callOnFrameDequeued = true;
+ bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
mCore->mIsAllocating = false;
@@ -608,6 +610,10 @@
} // Autolock scope
}
+ if (listener != nullptr && callOnFrameDequeued) {
+ listener->onFrameDequeued(bufferId);
+ }
+
if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
@@ -647,6 +653,8 @@
BQ_LOGV("detachBuffer: slot %d", slot);
sp<IConsumerListener> listener;
+ bool callOnFrameDetached = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameDetached is true
{
std::lock_guard<std::mutex> lock(mCore->mMutex);
@@ -684,8 +692,9 @@
listener = mCore->mConsumerListener;
auto gb = mSlots[slot].mGraphicBuffer;
- if (listener != nullptr && gb != nullptr) {
- listener->onFrameDetached(gb->getId());
+ if (gb != nullptr) {
+ callOnFrameDetached = true;
+ bufferId = gb->getId();
}
mSlots[slot].mBufferState.detachProducer();
mCore->mActiveBuffers.erase(slot);
@@ -695,6 +704,10 @@
VALIDATE_CONSISTENCY();
}
+ if (listener != nullptr && callOnFrameDetached) {
+ listener->onFrameDetached(bufferId);
+ }
+
if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -1105,58 +1118,71 @@
status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
ATRACE_CALL();
BQ_LOGV("cancelBuffer: slot %d", slot);
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
- return NO_INIT;
+ sp<IConsumerListener> listener;
+ bool callOnFrameCancelled = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameCancelled == true
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ if (mCore->mSharedBufferMode) {
+ BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode");
+ return BAD_VALUE;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
+ BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
+ "(state = %s)",
+ slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (fence == nullptr) {
+ BQ_LOGE("cancelBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mBufferState.cancel();
+
+ // After leaving shared buffer mode, the shared buffer will still be around.
+ // Mark it as no longer shared if this operation causes it to be free.
+ if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
+ mSlots[slot].mBufferState.mShared = false;
+ }
+
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[slot].mBufferState.isShared()) {
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeBuffers.push_back(slot);
+ }
+
+ auto gb = mSlots[slot].mGraphicBuffer;
+ if (gb != nullptr) {
+ callOnFrameCancelled = true;
+ bufferId = gb->getId();
+ }
+ mSlots[slot].mFence = fence;
+ mCore->mDequeueCondition.notify_all();
+ listener = mCore->mConsumerListener;
+ VALIDATE_CONSISTENCY();
}
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
- return NO_INIT;
+ if (listener != nullptr && callOnFrameCancelled) {
+ listener->onFrameCancelled(bufferId);
}
- if (mCore->mSharedBufferMode) {
- BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode");
- return BAD_VALUE;
- }
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isDequeued()) {
- BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (fence == nullptr) {
- BQ_LOGE("cancelBuffer: fence is NULL");
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.cancel();
-
- // After leaving shared buffer mode, the shared buffer will still be around.
- // Mark it as no longer shared if this operation causes it to be free.
- if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
- mSlots[slot].mBufferState.mShared = false;
- }
-
- // Don't put the shared buffer on the free list.
- if (!mSlots[slot].mBufferState.isShared()) {
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeBuffers.push_back(slot);
- }
-
- auto gb = mSlots[slot].mGraphicBuffer;
- if (mCore->mConsumerListener != nullptr && gb != nullptr) {
- mCore->mConsumerListener->onFrameCancelled(gb->getId());
- }
- mSlots[slot].mFence = fence;
- mCore->mDequeueCondition.notify_all();
- VALIDATE_CONSISTENCY();
-
return NO_ERROR;
}
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 6849a95..67cbc7b 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -99,7 +99,7 @@
if (mEventConnection != nullptr) {
auto status = mEventConnection->getLatestVsyncEventData(outVsyncEventData);
if (!status.isOk()) {
- ALOGE("Failed to get latest vsync event data: %s", status.exceptionMessage().c_str());
+ ALOGE("Failed to get latest vsync event data: %s", status.toString8().c_str());
return status.transactionError();
}
return NO_ERROR;
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index f3eb4e8..afb09de 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -255,7 +255,6 @@
uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
if (frame == nullptr) {
- ALOGE("updateAcquireFence: Did not find frame.");
return;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5bc05ef..db99726 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -377,7 +377,6 @@
}
auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
- ALOGE("cannot call null callback function, skipping");
continue;
}
std::vector<SurfaceControlStats> surfaceControlStats;
@@ -394,6 +393,11 @@
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
+
+ // More than one transaction may contain the same callback id. Erase the callback from
+ // the map to ensure that it is only called once. This can happen if transactions are
+ // parcelled out of process and applied in both processes.
+ callbacksMap.erase(callbackId);
}
// handle on complete callbacks
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index a4d9e77..a590c86 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/native/libs/nativewindow"
+ },
+ {
+ "path": "frameworks/native/services/surfaceflinger"
}
],
"presubmit": [
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 82e1b5a..073cc08 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -46,7 +46,7 @@
"android.hardware.configstore-utils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"android.hidl.token@1.0",
"libSurfaceFlingerProp",
"libgui",
@@ -72,6 +72,14 @@
"android-media-fuzzing-reports@google.com",
],
componentid: 155276,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libgui library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
@@ -82,6 +90,7 @@
],
defaults: [
"libgui_fuzzer_defaults",
+ "service_fuzzer_defaults",
],
}
@@ -92,6 +101,7 @@
],
defaults: [
"libgui_fuzzer_defaults",
+ "service_fuzzer_defaults",
],
}
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 95b7f39..3e37e48 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Boost.h>
#include <fuzzbinder/libbinder_driver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -39,10 +39,13 @@
ui::ColorMode::BT2100_HLG,
ui::ColorMode::DISPLAY_BT2020};
-constexpr hardware::power::Boost kBoost[] = {
- hardware::power::Boost::INTERACTION, hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
- hardware::power::Boost::ML_ACC, hardware::power::Boost::AUDIO_LAUNCH,
- hardware::power::Boost::CAMERA_LAUNCH, hardware::power::Boost::CAMERA_SHOT,
+constexpr aidl::android::hardware::power::Boost kBoost[] = {
+ aidl::android::hardware::power::Boost::INTERACTION,
+ aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
+ aidl::android::hardware::power::Boost::ML_ACC,
+ aidl::android::hardware::power::Boost::AUDIO_LAUNCH,
+ aidl::android::hardware::power::Boost::CAMERA_LAUNCH,
+ aidl::android::hardware::power::Boost::CAMERA_SHOT,
};
constexpr gui::TouchOcclusionMode kMode[] = {
@@ -284,7 +287,7 @@
SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>());
SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp));
- hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
+ aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
SurfaceComposerClient::notifyPowerBoost((int32_t)boostId);
String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 1dddeba..bf354e7 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -46,6 +46,8 @@
// where the previous frame was presented in the current frame's expected vsync. This pushes the
// current frame to the next vsync. The behavior is similar to BufferStuffing.
SurfaceFlingerStuffing = 0x100,
+ // Frame was dropped, as a newer frame was ready and replaced this frame.
+ Dropped = 0x200,
};
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 62e5f89..7aa7068 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -267,7 +267,8 @@
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
- layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged;
+ layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateSelectionPriority |
+ layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged |
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index cd90168..cce2892 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -351,6 +351,24 @@
sp<CountProducerListener> mProducerListener;
};
+// Helper class to ensure the provided BBQ frame number has been committed in SurfaceFlinger.
+class BBQSyncHelper {
+public:
+ BBQSyncHelper(BLASTBufferQueueHelper& bbqHelper, uint64_t frameNumber) {
+ t.addTransactionCompletedCallback(callbackHelper.function, callbackHelper.getContext());
+ bbqHelper.mergeWithNextTransaction(&t, frameNumber);
+ }
+
+ void wait() {
+ CallbackData callbackData;
+ callbackHelper.getCallbackData(&callbackData);
+ }
+
+private:
+ Transaction t;
+ CallbackHelper callbackHelper;
+};
+
TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
// create BLASTBufferQueue adapter associated with this surface
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
@@ -422,6 +440,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
@@ -447,7 +466,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -507,6 +526,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -531,7 +551,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -562,6 +582,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -590,7 +611,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
@@ -621,6 +642,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -653,7 +675,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -688,6 +710,7 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -720,7 +743,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ syncHelper.wait();
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
@@ -746,6 +769,7 @@
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
{
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -767,7 +791,7 @@
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
- adapter.waitForCallbacks();
+ syncHelper.wait();
}
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -780,6 +804,7 @@
adapter.update(mSurfaceControl, mDisplayWidth, mDisplayHeight / 2);
{
+ BBQSyncHelper syncHelper{adapter, 1};
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -802,7 +827,7 @@
NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
0, Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
- adapter.waitForCallbacks();
+ syncHelper.wait();
}
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index d585881..0168877 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -21,8 +21,10 @@
#include "MockConsumer.h"
#include <gui/BufferItem.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
@@ -37,6 +39,7 @@
#include <gtest/gtest.h>
+#include <future>
#include <thread>
using namespace std::chrono_literals;
@@ -1258,4 +1261,86 @@
ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
}
+class Latch {
+public:
+ explicit Latch(int expected) : mExpected(expected) {}
+ Latch(const Latch&) = delete;
+ Latch& operator=(const Latch&) = delete;
+
+ void CountDown() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mExpected--;
+ if (mExpected <= 0) {
+ mCV.notify_all();
+ }
+ }
+
+ void Wait() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mCV.wait(lock, [&] { return mExpected == 0; });
+ }
+
+private:
+ int mExpected;
+ std::mutex mLock;
+ std::condition_variable mCV;
+};
+
+struct OneshotOnDequeuedListener final : public BufferItemConsumer::FrameAvailableListener {
+ OneshotOnDequeuedListener(std::function<void()>&& oneshot)
+ : mOneshotRunnable(std::move(oneshot)) {}
+
+ std::function<void()> mOneshotRunnable;
+
+ void run() {
+ if (mOneshotRunnable) {
+ mOneshotRunnable();
+ mOneshotRunnable = nullptr;
+ }
+ }
+
+ void onFrameDequeued(const uint64_t) override { run(); }
+
+ void onFrameAvailable(const BufferItem&) override {}
+};
+
+// See b/270004534
+TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<BufferItemConsumer> bufferConsumer =
+ sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ ASSERT_NE(nullptr, bufferConsumer.get());
+ sp<Surface> surface = sp<Surface>::make(producer);
+ native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
+ native_window_set_buffers_dimensions(surface.get(), 100, 100);
+
+ Latch triggerDisconnect(1);
+ Latch resumeCallback(1);
+ auto luckyListener = sp<OneshotOnDequeuedListener>::make([&]() {
+ triggerDisconnect.CountDown();
+ resumeCallback.Wait();
+ });
+ bufferConsumer->setFrameAvailableListener(luckyListener);
+
+ std::future<void> disconnecter = std::async(std::launch::async, [&]() {
+ triggerDisconnect.Wait();
+ luckyListener = nullptr;
+ bufferConsumer = nullptr;
+ resumeCallback.CountDown();
+ });
+
+ std::future<void> render = std::async(std::launch::async, [=]() {
+ ANativeWindow_Buffer buffer;
+ surface->lock(&buffer, nullptr);
+ surface->unlockAndPost();
+ });
+
+ ASSERT_EQ(std::future_status::ready, render.wait_for(1s));
+ EXPECT_EQ(nullptr, luckyListener.get());
+ EXPECT_EQ(nullptr, bufferConsumer.get());
+}
+
} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 4d5bd5b..662e9fe 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,6 +24,7 @@
#include <memory>
+#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/keycodes.h>
#include <android/native_window.h>
@@ -74,6 +75,26 @@
static const int LAYER_BASE = INT32_MAX - 10;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener {
+public:
+ binder::Status onWindowInfosReported() override {
+ std::lock_guard<std::mutex> lock{mMutex};
+ mWindowInfosReported = true;
+ mConditionVariable.notify_one();
+ return binder::Status::ok();
+ }
+
+ void wait() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mConditionVariable.wait(lock, [&] { return mWindowInfosReported; });
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mConditionVariable;
+ bool mWindowInfosReported{false};
+};
+
class InputSurface {
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height, bool noInputChannel = false) {
@@ -264,7 +285,10 @@
t.setPosition(mSurfaceControl, x, y);
t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
- t.apply(true);
+ auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make();
+ t.addWindowInfosReportedListener(reportedListener);
+ t.apply();
+ reportedListener->wait();
}
void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8a17d8a..757cde2 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -65,6 +65,10 @@
bindgen_flags: [
"--verbose",
"--allowlist-var=AMOTION_EVENT_FLAG_CANCELED",
+ "--allowlist-var=AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED",
+ "--allowlist-var=AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED",
+ "--allowlist-var=AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT",
+ "--allowlist-var=AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE",
"--allowlist-var=AMOTION_EVENT_ACTION_CANCEL",
"--allowlist-var=AMOTION_EVENT_ACTION_UP",
"--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN",
@@ -109,62 +113,6 @@
],
}
-genrule {
- name: "libinput_cxx_bridge_code",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) >> $(out)",
- srcs: ["input_verifier.rs"],
- out: ["inputverifier_generated.cpp"],
-}
-
-genrule {
- name: "libinput_cxx_bridge_header",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) --header >> $(out)",
- srcs: ["input_verifier.rs"],
- out: ["input_verifier.rs.h"],
-}
-
-rust_defaults {
- name: "libinput_rust_defaults",
- srcs: ["input_verifier.rs"],
- host_supported: true,
- rustlibs: [
- "libbitflags",
- "libcxx",
- "libinput_bindgen",
- "liblogger",
- "liblog_rust",
- "inputconstants-rust",
- ],
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
-}
-
-rust_ffi_static {
- name: "libinput_rust",
- crate_name: "input",
- defaults: ["libinput_rust_defaults"],
-}
-
-rust_test {
- name: "libinput_rust_test",
- defaults: ["libinput_rust_defaults"],
- whole_static_libs: [
- "libinput_from_rust_to_cpp",
- ],
- test_options: {
- unit_test: true,
- },
- test_suites: ["device_tests"],
- sanitize: {
- hwaddress: true,
- },
-}
-
cc_library {
name: "libinput",
cpp_std: "c++20",
@@ -174,12 +122,18 @@
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
+ "-Wthread-safety",
+ "-Wshadow",
+ "-Wshadow-field-in-constructor-modified",
+ "-Wshadow-uncaptured-local",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
srcs: [
- "FromRustToCpp.cpp",
+ "android/os/IInputFlinger.aidl",
"Input.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputTransport.cpp",
"InputVerifier.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
@@ -208,14 +162,14 @@
"toolbox_input_labels",
],
- generated_sources: ["libinput_cxx_bridge_code"],
-
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"liblog",
"libPlatformProperties",
"libtinyxml2",
+ "libutils",
"libvintf",
],
@@ -231,15 +185,17 @@
static_libs: [
"inputconstants-cpp",
+ "libgui_window_info_static",
"libui-types",
"libtflite_static",
],
whole_static_libs: [
- "libinput_rust",
+ "libinput_rust_ffi",
],
export_static_lib_headers: [
+ "libgui_window_info_static",
"libui-types",
],
@@ -250,54 +206,15 @@
target: {
android: {
- srcs: [
- "InputTransport.cpp",
- "android/os/IInputFlinger.aidl",
- ],
-
- export_shared_lib_headers: ["libbinder"],
-
- shared_libs: [
- "libutils",
- "libbinder",
- ],
-
- static_libs: [
- "libgui_window_info_static",
- ],
-
- export_static_lib_headers: [
- "libgui_window_info_static",
- ],
-
required: [
"motion_predictor_model_prebuilt",
"motion_predictor_model_config",
],
},
host: {
- shared: {
- enabled: false,
- },
include_dirs: [
"bionic/libc/kernel/android/uapi/",
"bionic/libc/kernel/uapi",
- "frameworks/native/libs/arect/include",
- ],
- },
- host_linux: {
- srcs: [
- "InputTransport.cpp",
- ],
- static_libs: [
- "libgui_window_info_static",
- ],
- shared_libs: [
- "libbinder",
- ],
-
- export_static_lib_headers: [
- "libgui_window_info_static",
],
},
},
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 00925ba..c127411 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -497,19 +497,6 @@
}
}
-// --- PointerProperties ---
-
-bool PointerProperties::operator==(const PointerProperties& other) const {
- return id == other.id
- && toolType == other.toolType;
-}
-
-void PointerProperties::copyFrom(const PointerProperties& other) {
- id = other.id;
- toolType = other.toolType;
-}
-
-
// --- MotionEvent ---
void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index bade686..c218e1e 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -18,6 +18,7 @@
#include <linux/input-event-codes.h>
#include <linux/input.h>
+#include <strings.h>
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
@@ -523,6 +524,14 @@
return labels->name != nullptr ? labels->name : std::to_string(value);
}
+std::optional<int> getValue(const label* labels, const char* searchLabel) {
+ if (labels == nullptr) return {};
+ while (labels->name != nullptr && ::strcasecmp(labels->name, searchLabel) != 0) {
+ labels++;
+ }
+ return labels->name != nullptr ? std::make_optional(labels->value) : std::nullopt;
+}
+
const label* getCodeLabelsForType(int32_t type) {
switch (type) {
case EV_SYN:
@@ -556,7 +565,7 @@
if (type == EV_KEY) {
return ev_key_value_labels;
}
- if (type == EV_MSC && code == ABS_MT_TOOL_TYPE) {
+ if (type == EV_ABS && code == ABS_MT_TOOL_TYPE) {
return mt_tool_labels;
}
return nullptr;
@@ -572,4 +581,17 @@
};
}
+std::optional<int> InputEventLookup::getLinuxEvdevEventTypeByLabel(const char* label) {
+ return getValue(ev_labels, label);
+}
+
+std::optional<int> InputEventLookup::getLinuxEvdevEventCodeByLabel(int32_t type,
+ const char* label) {
+ return getValue(getCodeLabelsForType(type), label);
+}
+
+std::optional<int> InputEventLookup::getLinuxEvdevInputPropByLabel(const char* label) {
+ return getValue(input_prop_labels, label);
+}
+
} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 4d3d8bc..d9b7700 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -409,7 +409,7 @@
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
- sp<IBinder> token = new BBinder();
+ sp<IBinder> token = sp<BBinder>::make();
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
@@ -692,8 +692,8 @@
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
for (uint32_t i = 0; i < pointerCount; i++) {
- msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
- msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
+ msg.body.motion.pointers[i].properties = pointerProperties[i];
+ msg.body.motion.pointers[i].coords = pointerCoords[i];
}
return mChannel->sendMessage(&msg);
@@ -1270,13 +1270,13 @@
// We know here that the coordinates for the pointer haven't changed because we
// would've cleared the resampled bit in rewriteMessage if they had. We can't modify
// lastResample in place becasue the mapping from pointer ID to index may have changed.
- touchState.lastResample.pointers[i].copyFrom(oldLastResample.getPointerById(id));
+ touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id);
continue;
}
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
- resampledCoords.copyFrom(currentCoords);
+ resampledCoords = currentCoords;
if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
@@ -1454,8 +1454,8 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
- pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+ pointerProperties[i] = msg->body.motion.pointers[i].properties;
+ pointerCoords[i] = msg->body.motion.pointers[i].coords;
}
ui::Transform transform;
@@ -1484,7 +1484,7 @@
uint32_t pointerCount = msg->body.motion.pointerCount;
PointerCoords pointerCoords[pointerCount];
for (uint32_t i = 0; i < pointerCount; i++) {
- pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+ pointerCoords[i] = msg->body.motion.pointers[i].coords;
}
event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 32b4ca0..341eb6f 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -18,12 +18,14 @@
#include <android-base/logging.h>
#include <input/InputVerifier.h>
-#include "input_verifier.rs.h"
+#include "input_cxx_bridge.rs.h"
using android::base::Error;
using android::base::Result;
using android::input::RustPointerProperties;
+using DeviceId = int32_t;
+
namespace android {
// --- InputVerifier ---
@@ -31,7 +33,8 @@
InputVerifier::InputVerifier(const std::string& name)
: mVerifier(android::input::verifier::create(name)){};
-Result<void> InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
+Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
+ uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, int32_t flags) {
std::vector<RustPointerProperties> rpp;
@@ -41,7 +44,7 @@
rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()};
rust::String errorMessage =
android::input::verifier::process_movement(*mVerifier, deviceId, action, properties,
- flags);
+ static_cast<uint32_t>(flags));
if (errorMessage.empty()) {
return {};
} else {
@@ -49,4 +52,8 @@
}
}
+void InputVerifier::resetDevice(DeviceId deviceId) {
+ android::input::verifier::reset_device(*mVerifier, deviceId);
+}
+
} // namespace android
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index c2ea35c..0961a9d 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -25,9 +25,9 @@
#include <string>
#include <vector>
+#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/input.h>
-#include <log/log.h>
#include <attestation/HmacKeyManager.h>
#include <ftl/enum.h>
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index 5720099..c835a08 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -37,7 +37,7 @@
reset();
}
-VelocityControlParameters& VelocityControl::getParameters() {
+const VelocityControlParameters& VelocityControl::getParameters() const{
return mParameters;
}
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 8551e5f..8704eee 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -16,13 +16,13 @@
#define LOG_TAG "VelocityTracker"
-#include <array>
+#include <android-base/logging.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
+#include <array>
#include <optional>
-#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
#include <input/VelocityTracker.h>
#include <utils/BitSet.h>
@@ -56,6 +56,9 @@
// Nanoseconds per milliseconds.
static const nsecs_t NANOS_PER_MS = 1000000;
+// Seconds per nanosecond.
+static const float SECONDS_PER_NANO = 1E-9;
+
// All axes supported for velocity tracking, mapped to their default strategies.
// Although other strategies are available for testing and comparison purposes,
// the default strategy is the one that applications will actually use. Be very careful
@@ -241,6 +244,11 @@
void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis,
float position) {
+ if (pointerId < 0 || pointerId > MAX_POINTER_ID) {
+ LOG(FATAL) << "Invalid pointer ID " << pointerId << " for axis "
+ << MotionEvent::getLabel(axis);
+ }
+
if (mCurrentPointerIdBits.hasBit(pointerId) &&
std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) {
ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.",
@@ -268,12 +276,8 @@
", activePointerId=%s",
eventTime, pointerId, toString(mActivePointerId).c_str());
- std::optional<Estimator> estimator = getEstimator(axis, pointerId);
- ALOGD(" %d: axis=%d, position=%0.3f, "
- "estimator (degree=%d, coeff=%s, confidence=%f)",
- pointerId, axis, position, int((*estimator).degree),
- vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(),
- (*estimator).confidence);
+ ALOGD(" %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position,
+ toString(getVelocity(axis, pointerId)).c_str());
}
}
@@ -349,9 +353,9 @@
}
std::optional<float> VelocityTracker::getVelocity(int32_t axis, int32_t pointerId) const {
- std::optional<Estimator> estimator = getEstimator(axis, pointerId);
- if (estimator && (*estimator).degree >= 1) {
- return (*estimator).coeff[1];
+ const auto& it = mConfiguredStrategies.find(axis);
+ if (it != mConfiguredStrategies.end()) {
+ return it->second->getVelocity(pointerId);
}
return {};
}
@@ -374,56 +378,52 @@
return computedVelocity;
}
-std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis,
- int32_t pointerId) const {
- const auto& it = mConfiguredStrategies.find(axis);
- if (it == mConfiguredStrategies.end()) {
- return std::nullopt;
+AccumulatingVelocityTrackerStrategy::AccumulatingVelocityTrackerStrategy(
+ nsecs_t horizonNanos, bool maintainHorizonDuringAdd)
+ : mHorizonNanos(horizonNanos), mMaintainHorizonDuringAdd(maintainHorizonDuringAdd) {}
+
+void AccumulatingVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+ mMovements.erase(pointerId);
+}
+
+void AccumulatingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+ float position) {
+ auto [ringBufferIt, _] = mMovements.try_emplace(pointerId, HISTORY_SIZE);
+ RingBuffer<Movement>& movements = ringBufferIt->second;
+ const size_t size = movements.size();
+
+ if (size != 0 && movements[size - 1].eventTime == eventTime) {
+ // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
+ // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
+ // the new pointer. If the eventtimes for both events are identical, just update the data
+ // for this time (i.e. pop out the last element, and insert the updated movement).
+ // We only compare against the last value, as it is likely that addMovement is called
+ // in chronological order as events occur.
+ movements.popBack();
}
- return it->second->getEstimator(pointerId);
+
+ movements.pushBack({eventTime, position});
+
+ // Clear movements that do not fall within `mHorizonNanos` of the latest movement.
+ // Note that, if in the future we decide to use more movements (i.e. increase HISTORY_SIZE),
+ // we can consider making this step binary-search based, which will give us some improvement.
+ if (mMaintainHorizonDuringAdd) {
+ while (eventTime - movements[0].eventTime > mHorizonNanos) {
+ movements.popFront();
+ }
+ }
}
// --- LeastSquaresVelocityTrackerStrategy ---
LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree,
Weighting weighting)
- : mDegree(degree), mWeighting(weighting) {}
+ : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+ true /*maintainHorizonDuringAdd*/),
+ mDegree(degree),
+ mWeighting(weighting) {}
-LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
-}
-
-void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
- mIndex.erase(pointerId);
- mMovements.erase(pointerId);
-}
-
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
- float position) {
- // If data for this pointer already exists, we have a valid entry at the position of
- // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
- // to the next position in the circular buffer and write the new Movement there. Otherwise,
- // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
- // for this pointer and write to the first position.
- auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
- auto [indexIt, _] = mIndex.insert({pointerId, 0});
- size_t& index = indexIt->second;
- if (!inserted && movementIt->second[index].eventTime != eventTime) {
- // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
- // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
- // the new pointer. If the eventtimes for both events are identical, just update the data
- // for this time.
- // We only compare against the last value, as it is likely that addMovement is called
- // in chronological order as events occur.
- index++;
- }
- if (index == HISTORY_SIZE) {
- index = 0;
- }
-
- Movement& movement = movementIt->second[index];
- movement.eventTime = eventTime;
- movement.position = position;
-}
+LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {}
/**
* Solves a linear least squares problem to obtain a N degree polynomial that fits
@@ -474,10 +474,9 @@
* http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
* http://en.wikipedia.org/wiki/Gram-Schmidt
*/
-static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
- const std::vector<float>& w, uint32_t n,
- std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB,
- float* outDet) {
+static std::optional<float> solveLeastSquares(const std::vector<float>& x,
+ const std::vector<float>& y,
+ const std::vector<float>& w, uint32_t n) {
const size_t m = x.size();
ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
@@ -515,7 +514,7 @@
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
ALOGD_IF(DEBUG_STRATEGY, " - no solution, norm=%f", norm);
- return false;
+ return {};
}
float invNorm = 1.0f / norm;
@@ -549,6 +548,7 @@
for (uint32_t h = 0; h < m; h++) {
wy[h] = y[h] * w[h];
}
+ std::array<float, VelocityTracker::MAX_DEGREE + 1> outB;
for (uint32_t i = n; i != 0; ) {
i--;
outB[i] = vectorDot(&q[i][0], wy, m);
@@ -570,42 +570,46 @@
}
ymean /= m;
- float sserr = 0;
- float sstot = 0;
- for (uint32_t h = 0; h < m; h++) {
- float err = y[h] - outB[0];
- float term = 1;
- for (uint32_t i = 1; i < n; i++) {
- term *= x[h];
- err -= term * outB[i];
+ if (DEBUG_STRATEGY) {
+ float sserr = 0;
+ float sstot = 0;
+ for (uint32_t h = 0; h < m; h++) {
+ float err = y[h] - outB[0];
+ float term = 1;
+ for (uint32_t i = 1; i < n; i++) {
+ term *= x[h];
+ err -= term * outB[i];
+ }
+ sserr += w[h] * w[h] * err * err;
+ float var = y[h] - ymean;
+ sstot += w[h] * w[h] * var * var;
}
- sserr += w[h] * w[h] * err * err;
- float var = y[h] - ymean;
- sstot += w[h] * w[h] * var * var;
+ ALOGD(" - sserr=%f", sserr);
+ ALOGD(" - sstot=%f", sstot);
}
- *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
- ALOGD_IF(DEBUG_STRATEGY, " - sserr=%f", sserr);
- ALOGD_IF(DEBUG_STRATEGY, " - sstot=%f", sstot);
- ALOGD_IF(DEBUG_STRATEGY, " - det=%f", *outDet);
-
- return true;
+ return outB[1];
}
/*
* Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
* the default implementation
*/
-static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
- const std::vector<float>& x, const std::vector<float>& y) {
- const size_t count = x.size();
- LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes");
- // Solving y = a*x^2 + b*x + c
+std::optional<float> LeastSquaresVelocityTrackerStrategy::solveUnweightedLeastSquaresDeg2(
+ const RingBuffer<Movement>& movements) const {
+ // Solving y = a*x^2 + b*x + c, where
+ // - "x" is age (i.e. duration since latest movement) of the movemnets
+ // - "y" is positions of the movements.
float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
+ const size_t count = movements.size();
+ const Movement& newestMovement = movements[count - 1];
for (size_t i = 0; i < count; i++) {
- float xi = x[i];
- float yi = y[i];
+ const Movement& movement = movements[i];
+ nsecs_t age = newestMovement.eventTime - movement.eventTime;
+ float xi = -age * SECONDS_PER_NANO;
+ float yi = movement.position;
+
float xi2 = xi*xi;
float xi3 = xi2*xi;
float xi4 = xi3*xi;
@@ -632,124 +636,68 @@
ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
return std::nullopt;
}
- // Compute a
- float numerator = Sx2y*Sxx - Sxy*Sxx2;
- float a = numerator / denominator;
- // Compute b
- numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
- float b = numerator / denominator;
-
- // Compute c
- float c = syi/count - b * sxi/count - a * sxi2/count;
-
- return std::make_optional(std::array<float, 3>({c, b, a}));
+ return (Sxy * Sx2x2 - Sx2y * Sxx2) / denominator;
}
-std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator(
- int32_t pointerId) const {
+std::optional<float> LeastSquaresVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
const auto movementIt = mMovements.find(pointerId);
if (movementIt == mMovements.end()) {
return std::nullopt; // no data
}
+
+ const RingBuffer<Movement>& movements = movementIt->second;
+ const size_t size = movements.size();
+ if (size == 0) {
+ return std::nullopt; // no data
+ }
+
+ uint32_t degree = mDegree;
+ if (degree > size - 1) {
+ degree = size - 1;
+ }
+
+ if (degree <= 0) {
+ return std::nullopt;
+ }
+
+ if (degree == 2 && mWeighting == Weighting::NONE) {
+ // Optimize unweighted, quadratic polynomial fit
+ return solveUnweightedLeastSquaresDeg2(movements);
+ }
+
// Iterate over movement samples in reverse time order and collect samples.
std::vector<float> positions;
std::vector<float> w;
std::vector<float> time;
- uint32_t index = mIndex.at(pointerId);
- const Movement& newestMovement = movementIt->second[index];
- do {
- const Movement& movement = movementIt->second[index];
-
+ const Movement& newestMovement = movements[size - 1];
+ for (ssize_t i = size - 1; i >= 0; i--) {
+ const Movement& movement = movements[i];
nsecs_t age = newestMovement.eventTime - movement.eventTime;
- if (age > HORIZON) {
- break;
- }
- if (movement.eventTime == 0 && index != 0) {
- // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's
- // possible that not all entries are valid. We use a time=0 as a signal for those
- // uninitialized values. If we encounter a time of 0 in a position
- // that's > 0, it means that we hit the block where the data wasn't initialized.
- // We still don't know whether the value at index=0, with eventTime=0 is valid.
- // However, that's only possible when the value is by itself. So there's no hard in
- // processing it anyways, since the velocity for a single point is zero, and this
- // situation will only be encountered in artificial circumstances (in tests).
- // In practice, time will never be 0.
- break;
- }
positions.push_back(movement.position);
- w.push_back(chooseWeight(pointerId, index));
+ w.push_back(chooseWeight(pointerId, i));
time.push_back(-age * 0.000000001f);
- index = (index == 0 ? HISTORY_SIZE : index) - 1;
- } while (positions.size() < HISTORY_SIZE);
-
- const size_t m = positions.size();
- if (m == 0) {
- return std::nullopt; // no data
}
- // Calculate a least squares polynomial fit.
- uint32_t degree = mDegree;
- if (degree > m - 1) {
- degree = m - 1;
- }
-
- if (degree == 2 && mWeighting == Weighting::NONE) {
- // Optimize unweighted, quadratic polynomial fit
- std::optional<std::array<float, 3>> coeff =
- solveUnweightedLeastSquaresDeg2(time, positions);
- if (coeff) {
- VelocityTracker::Estimator estimator;
- estimator.time = newestMovement.eventTime;
- estimator.degree = 2;
- estimator.confidence = 1;
- for (size_t i = 0; i <= estimator.degree; i++) {
- estimator.coeff[i] = (*coeff)[i];
- }
- return estimator;
- }
- } else if (degree >= 1) {
- // General case for an Nth degree polynomial fit
- float det;
- uint32_t n = degree + 1;
- VelocityTracker::Estimator estimator;
- if (solveLeastSquares(time, positions, w, n, estimator.coeff, &det)) {
- estimator.time = newestMovement.eventTime;
- estimator.degree = degree;
- estimator.confidence = det;
-
- ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
- int(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(),
- estimator.confidence);
-
- return estimator;
- }
- }
-
- // No velocity data available for this pointer, but we do have its current position.
- VelocityTracker::Estimator estimator;
- estimator.coeff[0] = positions[0];
- estimator.time = newestMovement.eventTime;
- estimator.degree = 0;
- estimator.confidence = 1;
- return estimator;
+ // General case for an Nth degree polynomial fit
+ return solveLeastSquares(time, positions, w, degree + 1);
}
float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const {
- const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId);
+ const RingBuffer<Movement>& movements = mMovements.at(pointerId);
+ const size_t size = movements.size();
switch (mWeighting) {
case Weighting::DELTA: {
// Weight points based on how much time elapsed between them and the next
// point so that points that "cover" a shorter time span are weighed less.
// delta 0ms: 0.5
// delta 10ms: 1.0
- if (index == mIndex.at(pointerId)) {
+ if (index == size - 1) {
return 1.0f;
}
- uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
float deltaMillis =
- (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f;
+ (movements[index + 1].eventTime - movements[index].eventTime) * 0.000001f;
if (deltaMillis < 0) {
return 0.5f;
}
@@ -766,8 +714,7 @@
// age 50ms: 1.0
// age 60ms: 0.5
float ageMillis =
- (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
- 0.000001f;
+ (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f;
if (ageMillis < 0) {
return 0.5f;
}
@@ -789,8 +736,7 @@
// age 50ms: 1.0
// age 100ms: 0.5
float ageMillis =
- (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
- 0.000001f;
+ (movements[size - 1].eventTime - movements[index].eventTime) * 0.000001f;
if (ageMillis < 50) {
return 1.0f;
}
@@ -830,13 +776,9 @@
mPointerIdBits.markBit(pointerId);
}
-std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator(
- int32_t pointerId) const {
+std::optional<float> IntegratingVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
if (mPointerIdBits.hasBit(pointerId)) {
- const State& state = mPointerState[pointerId];
- VelocityTracker::Estimator estimator;
- populateEstimator(state, &estimator);
- return estimator;
+ return mPointerState[pointerId].vel;
}
return std::nullopt;
@@ -886,77 +828,39 @@
state.pos = pos;
}
-void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
- VelocityTracker::Estimator* outEstimator) const {
- outEstimator->time = state.updateTime;
- outEstimator->confidence = 1.0f;
- outEstimator->degree = state.degree;
- outEstimator->coeff[0] = state.pos;
- outEstimator->coeff[1] = state.vel;
- outEstimator->coeff[2] = state.accel / 2;
-}
-
-
// --- LegacyVelocityTrackerStrategy ---
-LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {}
+LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy()
+ : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+ false /*maintainHorizonDuringAdd*/) {}
LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
}
-void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
- mIndex.erase(pointerId);
- mMovements.erase(pointerId);
-}
-
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
- float position) {
- // If data for this pointer already exists, we have a valid entry at the position of
- // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
- // to the next position in the circular buffer and write the new Movement there. Otherwise,
- // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
- // for this pointer and write to the first position.
- auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
- auto [indexIt, _] = mIndex.insert({pointerId, 0});
- size_t& index = indexIt->second;
- if (!inserted && movementIt->second[index].eventTime != eventTime) {
- // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
- // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
- // the new pointer. If the eventtimes for both events are identical, just update the data
- // for this time.
- // We only compare against the last value, as it is likely that addMovement is called
- // in chronological order as events occur.
- index++;
- }
- if (index == HISTORY_SIZE) {
- index = 0;
- }
-
- Movement& movement = movementIt->second[index];
- movement.eventTime = eventTime;
- movement.position = position;
-}
-
-std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator(
- int32_t pointerId) const {
+std::optional<float> LegacyVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
const auto movementIt = mMovements.find(pointerId);
if (movementIt == mMovements.end()) {
return std::nullopt; // no data
}
- const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)];
+
+ const RingBuffer<Movement>& movements = movementIt->second;
+ const size_t size = movements.size();
+ if (size == 0) {
+ return std::nullopt; // no data
+ }
+
+ const Movement& newestMovement = movements[size - 1];
// Find the oldest sample that contains the pointer and that is not older than HORIZON.
nsecs_t minTime = newestMovement.eventTime - HORIZON;
- uint32_t oldestIndex = mIndex.at(pointerId);
- uint32_t numTouches = 1;
- do {
- uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
- const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex];
+ uint32_t oldestIndex = size - 1;
+ for (ssize_t i = size - 1; i >= 0; i--) {
+ const Movement& nextOldestMovement = movements[i];
if (nextOldestMovement.eventTime < minTime) {
break;
}
- oldestIndex = nextOldestIndex;
- } while (++numTouches < HISTORY_SIZE);
+ oldestIndex = i;
+ }
// Calculate an exponentially weighted moving average of the velocity estimate
// at different points in time measured relative to the oldest sample.
@@ -970,17 +874,13 @@
// 16ms apart but some consecutive samples could be only 0.5sm apart because
// the hardware or driver reports them irregularly or in bursts.
float accumV = 0;
- uint32_t index = oldestIndex;
uint32_t samplesUsed = 0;
- const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex];
+ const Movement& oldestMovement = movements[oldestIndex];
float oldestPosition = oldestMovement.position;
nsecs_t lastDuration = 0;
- while (numTouches-- > 1) {
- if (++index == HISTORY_SIZE) {
- index = 0;
- }
- const Movement& movement = mMovements.at(pointerId)[index];
+ for (size_t i = oldestIndex; i < size; i++) {
+ const Movement& movement = movements[i];
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
// If the duration between samples is small, we may significantly overestimate
@@ -996,62 +896,22 @@
}
}
- // Report velocity.
- float newestPosition = newestMovement.position;
- VelocityTracker::Estimator estimator;
- estimator.time = newestMovement.eventTime;
- estimator.confidence = 1;
- estimator.coeff[0] = newestPosition;
if (samplesUsed) {
- estimator.coeff[1] = accumV;
- estimator.degree = 1;
- } else {
- estimator.degree = 0;
+ return accumV;
}
- return estimator;
+ return std::nullopt;
}
// --- ImpulseVelocityTrackerStrategy ---
ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues)
- : mDeltaValues(deltaValues) {}
+ : AccumulatingVelocityTrackerStrategy(HORIZON /*horizonNanos*/,
+ true /*maintainHorizonDuringAdd*/),
+ mDeltaValues(deltaValues) {}
ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
}
-void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
- mIndex.erase(pointerId);
- mMovements.erase(pointerId);
-}
-
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
- float position) {
- // If data for this pointer already exists, we have a valid entry at the position of
- // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
- // to the next position in the circular buffer and write the new Movement there. Otherwise,
- // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
- // for this pointer and write to the first position.
- auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
- auto [indexIt, _] = mIndex.insert({pointerId, 0});
- size_t& index = indexIt->second;
- if (!inserted && movementIt->second[index].eventTime != eventTime) {
- // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
- // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
- // the new pointer. If the eventtimes for both events are identical, just update the data
- // for this time.
- // We only compare against the last value, as it is likely that addMovement is called
- // in chronological order as events occur.
- index++;
- }
- if (index == HISTORY_SIZE) {
- index = 0;
- }
-
- Movement& movement = movementIt->second[index];
- movement.eventTime = eventTime;
- movement.position = position;
-}
-
/**
* Calculate the total impulse provided to the screen and the resulting velocity.
*
@@ -1126,112 +986,44 @@
return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
}
-static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count,
- bool deltaValues) {
- // The input should be in reversed time order (most recent sample at index i=0)
- // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
- static constexpr float SECONDS_PER_NANO = 1E-9;
-
- if (count < 2) {
- return 0; // if 0 or 1 points, velocity is zero
- }
- if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
- ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
- }
-
- // If the data values are delta values, we do not have to calculate deltas here.
- // We can use the delta values directly, along with the calculated time deltas.
- // Since the data value input is in reversed time order:
- // [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1])
- // [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1])
- // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4].
- // Since the input is in reversed time order, the input values for this function would be
- // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively.
- //
- // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1}
- // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4}
-
- if (count == 2) { // if 2 points, basic linear calculation
- if (t[1] == t[0]) {
- ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
- return 0;
- }
- const float deltaX = deltaValues ? -x[0] : x[1] - x[0];
- return deltaX / (SECONDS_PER_NANO * (t[1] - t[0]));
- }
- // Guaranteed to have at least 3 points here
- float work = 0;
- for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time
- if (t[i] == t[i-1]) {
- ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]);
- continue;
- }
- float vprev = kineticEnergyToVelocity(work); // v[i-1]
- const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1];
- float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
- work += (vcurr - vprev) * fabsf(vcurr);
- if (i == count - 1) {
- work *= 0.5; // initial condition, case 2) above
- }
- }
- return kineticEnergyToVelocity(work);
-}
-
-std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator(
- int32_t pointerId) const {
+std::optional<float> ImpulseVelocityTrackerStrategy::getVelocity(int32_t pointerId) const {
const auto movementIt = mMovements.find(pointerId);
if (movementIt == mMovements.end()) {
return std::nullopt; // no data
}
- // Iterate over movement samples in reverse time order and collect samples.
- float positions[HISTORY_SIZE];
- nsecs_t time[HISTORY_SIZE];
- size_t m = 0; // number of points that will be used for fitting
- size_t index = mIndex.at(pointerId);
- const Movement& newestMovement = movementIt->second[index];
- do {
- const Movement& movement = movementIt->second[index];
-
- nsecs_t age = newestMovement.eventTime - movement.eventTime;
- if (age > HORIZON) {
- break;
- }
- if (movement.eventTime == 0 && index != 0) {
- // All eventTime's are initialized to 0. If we encounter a time of 0 in a position
- // that's >0, it means that we hit the block where the data wasn't initialized.
- // It's also possible that the sample at 0 would be invalid, but there's no harm in
- // processing it, since it would be just a single point, and will only be encountered
- // in artificial circumstances (in tests).
- break;
- }
-
- positions[m] = movement.position;
- time[m] = movement.eventTime;
- index = (index == 0 ? HISTORY_SIZE : index) - 1;
- } while (++m < HISTORY_SIZE);
-
- if (m == 0) {
+ const RingBuffer<Movement>& movements = movementIt->second;
+ const size_t size = movements.size();
+ if (size == 0) {
return std::nullopt; // no data
}
- VelocityTracker::Estimator estimator;
- estimator.coeff[0] = 0;
- estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
- estimator.coeff[2] = 0;
- estimator.time = newestMovement.eventTime;
- estimator.degree = 2; // similar results to 2nd degree fit
- estimator.confidence = 1;
+ float work = 0;
+ for (size_t i = 0; i < size - 1; i++) {
+ const Movement& mvt = movements[i];
+ const Movement& nextMvt = movements[i + 1];
- ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]);
+ float vprev = kineticEnergyToVelocity(work);
+ float delta = mDeltaValues ? nextMvt.position : nextMvt.position - mvt.position;
+ float vcurr = delta / (SECONDS_PER_NANO * (nextMvt.eventTime - mvt.eventTime));
+ work += (vcurr - vprev) * fabsf(vcurr);
+
+ if (i == 0) {
+ work *= 0.5; // initial condition, case 2) above
+ }
+ }
+
+ const float velocity = kineticEnergyToVelocity(work);
+ ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", velocity);
if (DEBUG_IMPULSE) {
// TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
// Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
// X axis chosen arbitrarily for velocity comparisons.
VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
- for (ssize_t i = m - 1; i >= 0; i--) {
- lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]);
+ for (size_t i = 0; i < size; i++) {
+ const Movement& mvt = movements[i];
+ lsq2.addMovement(mvt.eventTime, pointerId, AMOTION_EVENT_AXIS_X, mvt.position);
}
std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
if (v) {
@@ -1240,7 +1032,7 @@
ALOGD("lsq2 velocity: could not compute velocity");
}
}
- return estimator;
+ return velocity;
}
} // namespace android
diff --git a/libs/input/input_verifier.rs b/libs/input/input_verifier.rs
deleted file mode 100644
index 2e05a63..0000000
--- a/libs/input/input_verifier.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//! Validate the incoming motion stream.
-//! This class is not thread-safe.
-//! State is stored in the "InputVerifier" object
-//! that can be created via the 'create' method.
-//! Usage:
-//! Box<InputVerifier> verifier = create("inputChannel name");
-//! result = process_movement(verifier, ...);
-//! if (result) {
-//! crash(result.error_message());
-//! }
-
-use std::collections::HashMap;
-use std::collections::HashSet;
-
-use bitflags::bitflags;
-use log::info;
-
-#[cxx::bridge(namespace = "android::input")]
-mod ffi {
- #[namespace = "android"]
- unsafe extern "C++" {
- include!("ffi/FromRustToCpp.h");
- fn shouldLog(tag: &str) -> bool;
- }
- #[namespace = "android::input::verifier"]
- extern "Rust" {
- type InputVerifier;
-
- fn create(name: String) -> Box<InputVerifier>;
- fn process_movement(
- verifier: &mut InputVerifier,
- device_id: i32,
- action: u32,
- pointer_properties: &[RustPointerProperties],
- flags: i32,
- ) -> String;
- }
-
- pub struct RustPointerProperties {
- id: i32,
- }
-}
-
-use crate::ffi::shouldLog;
-use crate::ffi::RustPointerProperties;
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-struct DeviceId(i32);
-
-fn process_movement(
- verifier: &mut InputVerifier,
- device_id: i32,
- action: u32,
- pointer_properties: &[RustPointerProperties],
- flags: i32,
-) -> String {
- let result = verifier.process_movement(
- DeviceId(device_id),
- action,
- pointer_properties,
- Flags::from_bits(flags).unwrap(),
- );
- match result {
- Ok(()) => "".to_string(),
- Err(e) => e,
- }
-}
-
-fn create(name: String) -> Box<InputVerifier> {
- Box::new(InputVerifier::new(&name))
-}
-
-#[repr(u32)]
-enum MotionAction {
- Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
- Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
- Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
- PointerDown { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
- PointerUp { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
- HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
- HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
- HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
- Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
- ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-}
-
-fn get_action_index(action: u32) -> usize {
- let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- index.try_into().unwrap()
-}
-
-impl From<u32> for MotionAction {
- fn from(action: u32) -> Self {
- let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
- let action_index = get_action_index(action);
- match action_masked {
- input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
- input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
- input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
- input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
- input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
- input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
- MotionAction::PointerDown { action_index }
- }
- input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
- MotionAction::PointerUp { action_index }
- }
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
- input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
- input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
- input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
- _ => panic!("Unknown action: {}", action),
- }
- }
-}
-
-bitflags! {
- struct Flags: i32 {
- const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
- }
-}
-
-fn motion_action_to_string(action: u32) -> String {
- match action.into() {
- MotionAction::Down => "DOWN".to_string(),
- MotionAction::Up => "UP".to_string(),
- MotionAction::Move => "MOVE".to_string(),
- MotionAction::Cancel => "CANCEL".to_string(),
- MotionAction::Outside => "OUTSIDE".to_string(),
- MotionAction::PointerDown { action_index } => {
- format!("POINTER_DOWN({})", action_index)
- }
- MotionAction::PointerUp { action_index } => {
- format!("POINTER_UP({})", action_index)
- }
- MotionAction::HoverMove => "HOVER_MOVE".to_string(),
- MotionAction::Scroll => "SCROLL".to_string(),
- MotionAction::HoverEnter => "HOVER_ENTER".to_string(),
- MotionAction::HoverExit => "HOVER_EXIT".to_string(),
- MotionAction::ButtonPress => "BUTTON_PRESS".to_string(),
- MotionAction::ButtonRelease => "BUTTON_RELEASE".to_string(),
- }
-}
-
-/**
- * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
- * to inconsistent events.
- * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
- */
-fn log_events() -> bool {
- shouldLog("InputVerifierLogEvents")
-}
-
-struct InputVerifier {
- name: String,
- touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
-}
-
-impl InputVerifier {
- fn new(name: &str) -> Self {
- logger::init(
- logger::Config::default()
- .with_tag_on_device("InputVerifier")
- .with_min_level(log::Level::Trace),
- );
- Self { name: name.to_owned(), touching_pointer_ids_by_device: HashMap::new() }
- }
-
- fn process_movement(
- &mut self,
- device_id: DeviceId,
- action: u32,
- pointer_properties: &[RustPointerProperties],
- flags: Flags,
- ) -> Result<(), String> {
- if log_events() {
- info!(
- "Processing {} for device {:?} ({} pointer{}) on {}",
- motion_action_to_string(action),
- device_id,
- pointer_properties.len(),
- if pointer_properties.len() == 1 { "" } else { "s" },
- self.name
- );
- }
-
- match action.into() {
- MotionAction::Down => {
- let it = self
- .touching_pointer_ids_by_device
- .entry(device_id)
- .or_insert_with(HashSet::new);
- let pointer_id = pointer_properties[0].id;
- if it.contains(&pointer_id) {
- return Err(format!(
- "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
- self.name, device_id, it
- ));
- }
- it.insert(pointer_id);
- }
- MotionAction::PointerDown { action_index } => {
- if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
- return Err(format!(
- "{}: Received POINTER_DOWN but no pointers are currently down \
- for device {:?}",
- self.name, device_id
- ));
- }
- let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
- let pointer_id = pointer_properties[action_index].id;
- if it.contains(&pointer_id) {
- return Err(format!(
- "{}: Pointer with id={} not found in the properties",
- self.name, pointer_id
- ));
- }
- it.insert(pointer_id);
- }
- MotionAction::Move => {
- if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
- return Err(format!(
- "{}: ACTION_MOVE touching pointers don't match",
- self.name
- ));
- }
- }
- MotionAction::PointerUp { action_index } => {
- if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
- return Err(format!(
- "{}: Received POINTER_UP but no pointers are currently down for device \
- {:?}",
- self.name, device_id
- ));
- }
- let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
- let pointer_id = pointer_properties[action_index].id;
- it.remove(&pointer_id);
- }
- MotionAction::Up => {
- if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
- return Err(format!(
- "{} Received ACTION_UP but no pointers are currently down for device {:?}",
- self.name, device_id
- ));
- }
- let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
- if it.len() != 1 {
- return Err(format!(
- "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
- self.name, it, device_id
- ));
- }
- let pointer_id = pointer_properties[0].id;
- if !it.contains(&pointer_id) {
- return Err(format!(
- "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
- {:?} for device {:?}",
- self.name, pointer_id, it, device_id
- ));
- }
- it.clear();
- }
- MotionAction::Cancel => {
- if flags.contains(Flags::CANCELED) {
- return Err(format!(
- "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
- self.name
- ));
- }
- if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
- return Err(format!(
- "{}: Got ACTION_CANCEL, but the pointers don't match. \
- Existing pointers: {:?}",
- self.name, self.touching_pointer_ids_by_device
- ));
- }
- self.touching_pointer_ids_by_device.remove(&device_id);
- }
- _ => return Ok(()),
- }
- Ok(())
- }
-
- fn ensure_touching_pointers_match(
- &self,
- device_id: DeviceId,
- pointer_properties: &[RustPointerProperties],
- ) -> bool {
- let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
- return false;
- };
-
- for pointer_property in pointer_properties.iter() {
- let pointer_id = pointer_property.id;
- if !pointers.contains(&pointer_id) {
- return false;
- }
- }
- true
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::DeviceId;
- use crate::Flags;
- use crate::InputVerifier;
- use crate::RustPointerProperties;
- #[test]
- fn single_pointer_stream() {
- let mut verifier = InputVerifier::new("Test");
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- }
-
- #[test]
- fn multi_device_stream() {
- let mut verifier = InputVerifier::new("Test");
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- assert!(verifier
- .process_movement(
- DeviceId(2),
- input_bindgen::AMOTION_EVENT_ACTION_DOWN,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- assert!(verifier
- .process_movement(
- DeviceId(2),
- input_bindgen::AMOTION_EVENT_ACTION_MOVE,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- &pointer_properties,
- Flags::empty(),
- )
- .is_ok());
- }
-
- #[test]
- fn test_invalid_up() {
- let mut verifier = InputVerifier::new("Test");
- let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
- assert!(verifier
- .process_movement(
- DeviceId(1),
- input_bindgen::AMOTION_EVENT_ACTION_UP,
- &pointer_properties,
- Flags::empty(),
- )
- .is_err());
- }
-}
diff --git a/libs/input/rust/Android.bp b/libs/input/rust/Android.bp
new file mode 100644
index 0000000..018d199
--- /dev/null
+++ b/libs/input/rust/Android.bp
@@ -0,0 +1,72 @@
+// Copyright 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_defaults {
+ name: "libinput_rust_defaults",
+ crate_name: "input",
+ srcs: ["lib.rs"],
+ host_supported: true,
+ rustlibs: [
+ "libbitflags",
+ "libcxx",
+ "libinput_bindgen",
+ "liblogger",
+ "liblog_rust",
+ "inputconstants-rust",
+ ],
+ whole_static_libs: [
+ "libinput_from_rust_to_cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+rust_library {
+ name: "libinput_rust",
+ defaults: ["libinput_rust_defaults"],
+}
+
+rust_ffi_static {
+ name: "libinput_rust_ffi",
+ defaults: ["libinput_rust_defaults"],
+}
+
+rust_test {
+ name: "libinput_rust_test",
+ defaults: ["libinput_rust_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["device_tests"],
+ sanitize: {
+ hwaddress: true,
+ },
+}
+
+genrule {
+ name: "libinput_cxx_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["input_cxx_bridge_generated.cpp"],
+}
+
+genrule {
+ name: "libinput_cxx_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["input_cxx_bridge.rs.h"],
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
new file mode 100644
index 0000000..9d3b386
--- /dev/null
+++ b/libs/input/rust/input.rs
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Common definitions of the Android Input Framework in rust.
+
+use bitflags::bitflags;
+use std::fmt;
+
+/// The InputDevice ID.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct DeviceId(pub i32);
+
+/// A rust enum representation of a MotionEvent action.
+#[repr(u32)]
+pub enum MotionAction {
+ /// ACTION_DOWN
+ Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ /// ACTION_UP
+ Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
+ /// ACTION_MOVE
+ Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ /// ACTION_CANCEL
+ Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ /// ACTION_OUTSIDE
+ Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
+ /// ACTION_POINTER_DOWN
+ PointerDown {
+ /// The index of the affected pointer.
+ action_index: usize,
+ } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
+ /// ACTION_POINTER_UP
+ PointerUp {
+ /// The index of the affected pointer.
+ action_index: usize,
+ } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
+ /// ACTION_HOVER_ENTER
+ HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ /// ACTION_HOVER_MOVE
+ HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+ /// ACTION_HOVER_EXIT
+ HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+ /// ACTION_SCROLL
+ Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
+ /// ACTION_BUTTON_PRESS
+ ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ /// ACTION_BUTTON_RELEASE
+ ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+}
+
+impl fmt::Display for MotionAction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ MotionAction::Down => write!(f, "DOWN"),
+ MotionAction::Up => write!(f, "UP"),
+ MotionAction::Move => write!(f, "MOVE"),
+ MotionAction::Cancel => write!(f, "CANCEL"),
+ MotionAction::Outside => write!(f, "OUTSIDE"),
+ MotionAction::PointerDown { action_index } => {
+ write!(f, "POINTER_DOWN({})", action_index)
+ }
+ MotionAction::PointerUp { action_index } => write!(f, "POINTER_UP({})", action_index),
+ MotionAction::HoverMove => write!(f, "HOVER_MOVE"),
+ MotionAction::Scroll => write!(f, "SCROLL"),
+ MotionAction::HoverEnter => write!(f, "HOVER_ENTER"),
+ MotionAction::HoverExit => write!(f, "HOVER_EXIT"),
+ MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"),
+ MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"),
+ }
+ }
+}
+
+impl From<u32> for MotionAction {
+ fn from(action: u32) -> Self {
+ let (action_masked, action_index) = MotionAction::breakdown_action(action);
+ match action_masked {
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
+ input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
+ input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
+ MotionAction::PointerDown { action_index }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
+ MotionAction::PointerUp { action_index }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
+ input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+ _ => panic!("Unknown action: {}", action),
+ }
+ }
+}
+
+impl MotionAction {
+ fn breakdown_action(action: u32) -> (u32, usize) {
+ let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
+ let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ (action_masked, index.try_into().unwrap())
+ }
+}
+
+bitflags! {
+ /// MotionEvent flags.
+ pub struct MotionFlags: u32 {
+ /// FLAG_CANCELED
+ const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED as u32;
+ /// FLAG_WINDOW_IS_OBSCURED
+ const WINDOW_IS_OBSCURED = input_bindgen::AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ /// FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+ const WINDOW_IS_PARTIALLY_OBSCURED =
+ input_bindgen::AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ /// FLAG_IS_ACCESSIBILITY_EVENT
+ const IS_ACCESSIBILITY_EVENT =
+ input_bindgen::AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+ /// FLAG_NO_FOCUS_CHANGE
+ const NO_FOCUS_CHANGE = input_bindgen::AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
new file mode 100644
index 0000000..64c0466
--- /dev/null
+++ b/libs/input/rust/input_verifier.rs
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Contains the InputVerifier, used to validate a stream of input events.
+
+use crate::ffi::RustPointerProperties;
+use crate::input::{DeviceId, MotionAction, MotionFlags};
+use log::info;
+use std::collections::HashMap;
+use std::collections::HashSet;
+
+/// The InputVerifier is used to validate a stream of input events.
+pub struct InputVerifier {
+ name: String,
+ should_log: bool,
+ touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+ hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+}
+
+impl InputVerifier {
+ /// Create a new InputVerifier.
+ pub fn new(name: &str, should_log: bool) -> Self {
+ logger::init(
+ logger::Config::default()
+ .with_tag_on_device("InputVerifier")
+ .with_min_level(log::Level::Trace),
+ );
+ Self {
+ name: name.to_owned(),
+ should_log,
+ touching_pointer_ids_by_device: HashMap::new(),
+ hovering_pointer_ids_by_device: HashMap::new(),
+ }
+ }
+
+ /// Process a pointer movement event from an InputDevice.
+ /// If the event is not valid, we return an error string that describes the issue.
+ pub fn process_movement(
+ &mut self,
+ device_id: DeviceId,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: MotionFlags,
+ ) -> Result<(), String> {
+ if self.should_log {
+ info!(
+ "Processing {} for device {:?} ({} pointer{}) on {}",
+ MotionAction::from(action).to_string(),
+ device_id,
+ pointer_properties.len(),
+ if pointer_properties.len() == 1 { "" } else { "s" },
+ self.name
+ );
+ }
+
+ match action.into() {
+ MotionAction::Down => {
+ let it = self
+ .touching_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ let pointer_id = pointer_properties[0].id;
+ if it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
+ self.name, device_id, it
+ ));
+ }
+ it.insert(pointer_id);
+ }
+ MotionAction::PointerDown { action_index } => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Received POINTER_DOWN but no pointers are currently down \
+ for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = pointer_properties[action_index].id;
+ if it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Pointer with id={} not found in the properties",
+ self.name, pointer_id
+ ));
+ }
+ it.insert(pointer_id);
+ }
+ MotionAction::Move => {
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ return Err(format!(
+ "{}: ACTION_MOVE touching pointers don't match",
+ self.name
+ ));
+ }
+ }
+ MotionAction::PointerUp { action_index } => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Received POINTER_UP but no pointers are currently down for device \
+ {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = pointer_properties[action_index].id;
+ it.remove(&pointer_id);
+ }
+ MotionAction::Up => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{} Received ACTION_UP but no pointers are currently down for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ if it.len() != 1 {
+ return Err(format!(
+ "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
+ self.name, it, device_id
+ ));
+ }
+ let pointer_id = pointer_properties[0].id;
+ if !it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
+ {:?} for device {:?}",
+ self.name, pointer_id, it, device_id
+ ));
+ }
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ }
+ MotionAction::Cancel => {
+ if !flags.contains(MotionFlags::CANCELED) {
+ return Err(format!(
+ "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
+ self.name
+ ));
+ }
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ return Err(format!(
+ "{}: Got ACTION_CANCEL, but the pointers don't match. \
+ Existing pointers: {:?}",
+ self.name, self.touching_pointer_ids_by_device
+ ));
+ }
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ }
+ /*
+ * The hovering protocol currently supports a single pointer only, because we do not
+ * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
+ * Still, we are keeping the infrastructure here pretty general in case that is
+ * eventually supported.
+ */
+ MotionAction::HoverEnter => {
+ if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
+ {:?}",
+ self.name, device_id, self.hovering_pointer_ids_by_device
+ ));
+ }
+ let it = self
+ .hovering_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ it.insert(pointer_properties[0].id);
+ }
+ MotionAction::HoverMove => {
+ // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
+ // If there was no prior HOVER_ENTER, just start a new hovering pointer.
+ let it = self
+ .hovering_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ it.insert(pointer_properties[0].id);
+ }
+ MotionAction::HoverExit => {
+ if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let pointer_id = pointer_properties[0].id;
+ let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ it.remove(&pointer_id);
+
+ if !it.is_empty() {
+ return Err(format!(
+ "{}: Removed hovering pointer {}, but pointers are still\
+ hovering for device {:?}: {:?}",
+ self.name, pointer_id, device_id, it
+ ));
+ }
+ self.hovering_pointer_ids_by_device.remove(&device_id);
+ }
+ _ => return Ok(()),
+ }
+ Ok(())
+ }
+
+ /// Notify the verifier that the device has been reset, which will cause the verifier to erase
+ /// the current internal state for this device. Subsequent events from this device are expected
+ //// to start a new gesture.
+ pub fn reset_device(&mut self, device_id: DeviceId) {
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ self.hovering_pointer_ids_by_device.remove(&device_id);
+ }
+
+ fn ensure_touching_pointers_match(
+ &self,
+ device_id: DeviceId,
+ pointer_properties: &[RustPointerProperties],
+ ) -> bool {
+ let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
+ return false;
+ };
+
+ for pointer_property in pointer_properties.iter() {
+ let pointer_id = pointer_property.id;
+ if !pointers.contains(&pointer_id) {
+ return false;
+ }
+ }
+ true
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_verifier::InputVerifier;
+ use crate::DeviceId;
+ use crate::MotionFlags;
+ use crate::RustPointerProperties;
+ #[test]
+ fn single_pointer_stream() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn multi_device_stream() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn action_cancel() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ &pointer_properties,
+ MotionFlags::CANCELED,
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn invalid_action_cancel() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ &pointer_properties,
+ MotionFlags::empty(), // forgot to set FLAG_CANCELED
+ )
+ .is_err());
+ }
+
+ #[test]
+ fn invalid_up() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
+
+ #[test]
+ fn correct_hover_sequence() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn double_hover_enter() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
new file mode 100644
index 0000000..688d941
--- /dev/null
+++ b/libs/input/rust/lib.rs
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! The rust component of libinput.
+
+mod input;
+mod input_verifier;
+
+pub use input::{DeviceId, MotionAction, MotionFlags};
+pub use input_verifier::InputVerifier;
+
+#[cxx::bridge(namespace = "android::input")]
+mod ffi {
+ #[namespace = "android"]
+ unsafe extern "C++" {
+ include!("ffi/FromRustToCpp.h");
+ fn shouldLog(tag: &str) -> bool;
+ }
+
+ #[namespace = "android::input::verifier"]
+ extern "Rust" {
+ /// Used to validate the incoming motion stream.
+ /// This class is not thread-safe.
+ /// State is stored in the "InputVerifier" object
+ /// that can be created via the 'create' method.
+ /// Usage:
+ ///
+ /// ```ignore
+ /// Box<InputVerifier> verifier = create("inputChannel name");
+ /// result = process_movement(verifier, ...);
+ /// if (result) {
+ /// crash(result.error_message());
+ /// }
+ /// ```
+ type InputVerifier;
+ fn create(name: String) -> Box<InputVerifier>;
+ fn process_movement(
+ verifier: &mut InputVerifier,
+ device_id: i32,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: u32,
+ ) -> String;
+ fn reset_device(verifier: &mut InputVerifier, device_id: i32);
+ }
+
+ #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+ pub struct RustPointerProperties {
+ pub id: i32,
+ }
+}
+
+use crate::ffi::RustPointerProperties;
+
+fn create(name: String) -> Box<InputVerifier> {
+ Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
+}
+
+fn process_movement(
+ verifier: &mut InputVerifier,
+ device_id: i32,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: u32,
+) -> String {
+ let result = verifier.process_movement(
+ DeviceId(device_id),
+ action,
+ pointer_properties,
+ MotionFlags::from_bits(flags).unwrap(),
+ );
+ match result {
+ Ok(()) => "".to_string(),
+ Err(e) => e,
+ }
+}
+
+fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
+ verifier.reset_device(DeviceId(device_id));
+}
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index ae72109..ffebff1 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -42,8 +42,8 @@
// here EV = expected value, tol = VELOCITY_TOLERANCE
constexpr float VELOCITY_TOLERANCE = 0.2;
-// estimate coefficients must be within 0.001% of the target value
-constexpr float COEFFICIENT_TOLERANCE = 0.00001;
+// quadratic velocity must be within 0.001% of the target value
+constexpr float QUADRATIC_VELOCITY_TOLERANCE = 0.00001;
// --- VelocityTrackerTest ---
class VelocityTrackerTest : public testing::Test { };
@@ -76,10 +76,6 @@
}
}
-static void checkCoefficient(float actual, float target) {
- EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE);
-}
-
struct Position {
float x;
float y;
@@ -284,21 +280,20 @@
checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
}
-static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions,
- const std::array<float, 3>& coefficients) {
+static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
+ float velocity) {
VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
for (MotionEvent event : events) {
vt.addMovement(&event);
}
- std::optional<VelocityTracker::Estimator> estimatorX = vt.getEstimator(AMOTION_EVENT_AXIS_X, 0);
- std::optional<VelocityTracker::Estimator> estimatorY = vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0);
- EXPECT_TRUE(estimatorX);
- EXPECT_TRUE(estimatorY);
- for (size_t i = 0; i< coefficients.size(); i++) {
- checkCoefficient((*estimatorX).coeff[i], coefficients[i]);
- checkCoefficient((*estimatorY).coeff[i], coefficients[i]);
- }
+ std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0);
+ std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
+ ASSERT_TRUE(velocityX);
+ ASSERT_TRUE(velocityY);
+
+ EXPECT_NEAR_BY_FRACTION(*velocityX, velocity, QUADRATIC_VELOCITY_TOLERANCE);
+ EXPECT_NEAR_BY_FRACTION(*velocityY, velocity, QUADRATIC_VELOCITY_TOLERANCE);
}
/*
@@ -461,8 +456,6 @@
EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
- EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
-
VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000);
for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id));
@@ -1074,7 +1067,7 @@
* If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be
* part of the fitted data), this can cause large velocity values to be reported instead.
*/
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_ThreeFingerTap) {
std::vector<PlanarMotionEventEntry> motions = {
{ 0us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
{ 10800us, {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN
@@ -1162,7 +1155,7 @@
* ================== Tests for least squares fitting ==============================================
*
* Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy
- * getEstimator function. In particular:
+ * getVelocity function. In particular:
* - inside the function, time gets converted from nanoseconds to seconds
* before being used in the fit.
* - any values that are older than 100 ms are being discarded.
@@ -1183,7 +1176,7 @@
* The coefficients are (0, 0, 1).
* In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
*/
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Constant) {
std::vector<PlanarMotionEventEntry> motions = {
{ 0ms, {{1, 1}} }, // 0 s
{ 1ms, {{1, 1}} }, // 0.001 s
@@ -1195,13 +1188,13 @@
// -0.002, 1
// -0.001, 1
// -0.ms, 1
- computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({1, 0, 0}));
+ computeAndCheckQuadraticVelocity(motions, 0);
}
/*
* Straight line y = x :: the constant and quadratic coefficients are zero.
*/
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Linear) {
std::vector<PlanarMotionEventEntry> motions = {
{ 0ms, {{-2, -2}} },
{ 1ms, {{-1, -1}} },
@@ -1213,13 +1206,13 @@
// -0.002, -2
// -0.001, -1
// -0.000, 0
- computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 1E3, 0}));
+ computeAndCheckQuadraticVelocity(motions, 1E3);
}
/*
* Parabola
*/
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic) {
std::vector<PlanarMotionEventEntry> motions = {
{ 0ms, {{1, 1}} },
{ 1ms, {{4, 4}} },
@@ -1231,13 +1224,13 @@
// -0.002, 1
// -0.001, 4
// -0.000, 8
- computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({8, 4.5E3, 0.5E6}));
+ computeAndCheckQuadraticVelocity(motions, 4.5E3);
}
/*
* Parabola
*/
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic2) {
std::vector<PlanarMotionEventEntry> motions = {
{ 0ms, {{1, 1}} },
{ 1ms, {{4, 4}} },
@@ -1249,13 +1242,13 @@
// -0.002, 1
// -0.001, 4
// -0.000, 9
- computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({9, 6E3, 1E6}));
+ computeAndCheckQuadraticVelocity(motions, 6E3);
}
/*
* Parabola :: y = x^2 :: the constant and linear coefficients are zero.
*/
-TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
+TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategy_Parabolic3) {
std::vector<PlanarMotionEventEntry> motions = {
{ 0ms, {{4, 4}} },
{ 1ms, {{1, 1}} },
@@ -1267,7 +1260,7 @@
// -0.002, 4
// -0.001, 1
// -0.000, 0
- computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6}));
+ computeAndCheckQuadraticVelocity(motions, 0E3);
}
// Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity.
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 8060705..e7b2195 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -40,6 +40,80 @@
using namespace android;
// ----------------------------------------------------------------------------
+// Validate hardware_buffer.h and PixelFormat.aidl agree
+// ----------------------------------------------------------------------------
+
+static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_16 == AHARDWAREBUFFER_FORMAT_D16_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_24 == AHARDWAREBUFFER_FORMAT_D24_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_32F == AHARDWAREBUFFER_FORMAT_D32_FLOAT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_STENCIL_8 == AHARDWAREBUFFER_FORMAT_S8_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED ==
+ AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(HAL_PIXEL_FORMAT_YCBCR_P010 == AHARDWAREBUFFER_FORMAT_YCbCr_P010,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
+ AHARDWAREBUFFER_FORMAT_R8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) ==
+ AHARDWAREBUFFER_FORMAT_R16_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(
+ static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) ==
+ AHARDWAREBUFFER_FORMAT_R16G16_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+static_assert(
+ static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
+ AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+
+// ----------------------------------------------------------------------------
// Public functions
// ----------------------------------------------------------------------------
@@ -227,11 +301,14 @@
}
return result;
} else {
- const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format);
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ int result = gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence,
+ &bytesPerPixel, &bytesPerStride);
outPlanes->planeCount = 1;
- outPlanes->planes[0].pixelStride = pixelStride;
- outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride;
- return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence);
+ outPlanes->planes[0].pixelStride = bytesPerPixel;
+ outPlanes->planes[0].rowStride = bytesPerStride;
+ return result;
}
}
@@ -487,12 +564,6 @@
return false;
}
- if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
- ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))",
- desc->format, desc->format);
- return false;
- }
-
if (desc->rfu0 != 0 || desc->rfu1 != 0) {
ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0");
return false;
@@ -557,114 +628,6 @@
return true;
}
-bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
- static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_16 == AHARDWAREBUFFER_FORMAT_D16_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_24 == AHARDWAREBUFFER_FORMAT_D24_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_32F == AHARDWAREBUFFER_FORMAT_D32_FLOAT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_STENCIL_8 == AHARDWAREBUFFER_FORMAT_S8_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED == AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(HAL_PIXEL_FORMAT_YCBCR_P010 == AHARDWAREBUFFER_FORMAT_YCbCr_P010,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
- AHARDWAREBUFFER_FORMAT_R8_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) ==
- AHARDWAREBUFFER_FORMAT_R16_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) ==
- AHARDWAREBUFFER_FORMAT_R16G16_UINT,
- "HAL and AHardwareBuffer pixel format don't match");
- static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
- AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
- "HAL and AHardwareBuffer pixel format don't match");
-
- switch (format) {
- case AHARDWAREBUFFER_FORMAT_R8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R16_UINT:
- case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
- case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
- case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
- case AHARDWAREBUFFER_FORMAT_BLOB:
- case AHARDWAREBUFFER_FORMAT_D16_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
- case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
- // VNDK formats only -- unfortunately we can't differentiate from where we're called
- case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
- case AHARDWAREBUFFER_FORMAT_YV12:
- case AHARDWAREBUFFER_FORMAT_Y8:
- case AHARDWAREBUFFER_FORMAT_Y16:
- case AHARDWAREBUFFER_FORMAT_RAW16:
- case AHARDWAREBUFFER_FORMAT_RAW10:
- case AHARDWAREBUFFER_FORMAT_RAW12:
- case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
- case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
- case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
- case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
- case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
- case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
- return true;
-
- default:
- return false;
- }
-}
-
bool AHardwareBuffer_formatIsYuv(uint32_t format) {
switch (format) {
case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
@@ -681,32 +644,6 @@
}
}
-uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
- switch (format) {
- case AHARDWAREBUFFER_FORMAT_R8_UNORM:
- return 1;
- case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
- case AHARDWAREBUFFER_FORMAT_D16_UNORM:
- case AHARDWAREBUFFER_FORMAT_R16_UINT:
- return 2;
- case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM:
- return 3;
- case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
- case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
- case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
- case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
- case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
- return 4;
- case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
- case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
- return 8;
- default:
- return 0;
- }
-}
-
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
return hal_format;
}
diff --git a/libs/nativewindow/TEST_MAPPING b/libs/nativewindow/TEST_MAPPING
index 3d7f3c2..9d6425b 100644
--- a/libs/nativewindow/TEST_MAPPING
+++ b/libs/nativewindow/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit": [
{
+ "name": "libnativewindow_bindgen_test"
+ },
+ {
"name": "libnativewindow_test"
+ },
+ {
+ "name": "libnativewindow_rs-internal_test"
}
]
}
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 6d3d295..880c694 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -37,15 +37,9 @@
// parameters. Note: this does not verify any platform-specific contraints.
bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log);
-// whether this AHardwareBuffer format is valid
-bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
-
// whether this is a YUV type format
bool AHardwareBuffer_formatIsYuv(uint32_t format);
-// number of bytes per pixel or 0 if unknown or multi-planar
-uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format);
-
// convert AHardwareBuffer format to HAL format (note: this is a no-op)
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index ad4cc4a..9fa5569 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -450,7 +450,7 @@
*
* Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
*/
- ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
/**
* Adobe RGB
@@ -471,21 +471,21 @@
ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
/**
+ * ITU-R Recommendation 601 (BT.601) - 625-line
+ *
+ * Standard-definition television, 625 Lines (PAL)
+ *
+ * Use limited range, SMPTE 170M transfer and BT.601_625 standard.
+ */
+ ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
* ITU-R Recommendation 601 (BT.601) - 525-line
*
* Standard-definition television, 525 Lines (NTSC)
*
* Use limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
- ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
-
- /**
- * ITU-R Recommendation 709 (BT.709)
- *
- * High-definition television
- *
- * Use limited range, SMPTE 170M transfer and BT.709 standard.
- */
ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index c2fd6ef..dcb5068 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -65,7 +65,6 @@
LIBNATIVEWINDOW_PLATFORM {
global:
extern "C++" {
- android::AHardwareBuffer_isValidPixelFormat*;
android::AHardwareBuffer_convertFromPixelFormat*;
android::AHardwareBuffer_convertToPixelFormat*;
android::AHardwareBuffer_convertFromGrallocUsageBits*;
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
new file mode 100644
index 0000000..dc1575c
--- /dev/null
+++ b/libs/nativewindow/rust/Android.bp
@@ -0,0 +1,87 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: [
+ "frameworks_native_libs_nativewindow_license",
+ ],
+}
+
+rust_bindgen {
+ name: "libnativewindow_bindgen",
+ crate_name: "nativewindow_bindgen",
+ wrapper_src: "sys/nativewindow_bindings.h",
+ source_stem: "bindings",
+ bindgen_flags: [
+ "--constified-enum-module=AHardwareBuffer_Format",
+ "--bitfield-enum=AHardwareBuffer_UsageFlags",
+
+ "--allowlist-file=.*/nativewindow/include/.*\\.h",
+
+ "--with-derive-eq",
+ "--with-derive-partialeq",
+ ],
+ shared_libs: [
+ "libnativewindow",
+ ],
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+}
+
+rust_test {
+ name: "libnativewindow_bindgen_test",
+ srcs: [":libnativewindow_bindgen"],
+ crate_name: "nativewindow_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
+
+rust_defaults {
+ name: "libnativewindow_defaults",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libnativewindow_bindgen",
+ ],
+}
+
+rust_library {
+ name: "libnativewindow_rs",
+ crate_name: "nativewindow",
+ defaults: ["libnativewindow_defaults"],
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+}
+
+rust_test {
+ name: "libnativewindow_rs-internal_test",
+ crate_name: "nativewindow",
+ defaults: ["libnativewindow_defaults"],
+ test_suites: ["general-tests"],
+}
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
new file mode 100644
index 0000000..a5bcc62
--- /dev/null
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -0,0 +1,260 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Pleasant Rust bindings for libnativewindow, including AHardwareBuffer
+
+extern crate nativewindow_bindgen as ffi;
+
+pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+
+use std::os::raw::c_void;
+use std::ptr;
+
+/// Wrapper around an opaque C AHardwareBuffer.
+pub struct AHardwareBuffer(*mut ffi::AHardwareBuffer);
+
+impl AHardwareBuffer {
+ /// Test whether the given format and usage flag combination is allocatable. If this function
+ /// returns true, it means that a buffer with the given description can be allocated on this
+ /// implementation, unless resource exhaustion occurs. If this function returns false, it means
+ /// that the allocation of the given description will never succeed.
+ ///
+ /// Available since API 29
+ pub fn is_supported(
+ width: u32,
+ height: u32,
+ layers: u32,
+ format: AHardwareBuffer_Format::Type,
+ usage: AHardwareBuffer_UsageFlags,
+ stride: u32,
+ ) -> bool {
+ let buffer_desc = ffi::AHardwareBuffer_Desc {
+ width,
+ height,
+ layers,
+ format,
+ usage: usage.0,
+ stride,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ // SAFETY: *buffer_desc will never be null.
+ let status = unsafe { ffi::AHardwareBuffer_isSupported(&buffer_desc) };
+
+ status == 1
+ }
+
+ /// Allocates a buffer that matches the passed AHardwareBuffer_Desc. If allocation succeeds, the
+ /// buffer can be used according to the usage flags specified in its description. If a buffer is
+ /// used in ways not compatible with its usage flags, the results are undefined and may include
+ /// program termination.
+ ///
+ /// Available since API level 26.
+ pub fn new(
+ width: u32,
+ height: u32,
+ layers: u32,
+ format: AHardwareBuffer_Format::Type,
+ usage: AHardwareBuffer_UsageFlags,
+ ) -> Option<Self> {
+ let buffer_desc = ffi::AHardwareBuffer_Desc {
+ width,
+ height,
+ layers,
+ format,
+ usage: usage.0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ let mut buffer = ptr::null_mut();
+ // SAFETY: The returned pointer is valid until we drop/deallocate it. The function may fail
+ // and return a status, but we check it later.
+ let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut buffer) };
+
+ if status == 0 {
+ Some(Self(buffer))
+ } else {
+ None
+ }
+ }
+
+ /// Adopts the raw pointer and wraps it in a Rust AHardwareBuffer.
+ ///
+ /// # Errors
+ ///
+ /// Will panic if buffer_ptr is null.
+ ///
+ /// # Safety
+ ///
+ /// This function adopts the pointer but does NOT increment the refcount on the buffer. If the
+ /// caller uses the pointer after the created object is dropped it will cause a memory leak.
+ pub unsafe fn take_from_raw(buffer_ptr: *mut c_void) -> Self {
+ assert!(!buffer_ptr.is_null());
+ Self(buffer_ptr as *mut ffi::AHardwareBuffer)
+ }
+
+ /// Get the system wide unique id for an AHardwareBuffer. This function may panic in extreme
+ /// and undocumented circumstances.
+ ///
+ /// Available since API level 31.
+ pub fn id(&self) -> u64 {
+ let mut out_id = 0;
+ // SAFETY: Neither pointers can be null.
+ let status = unsafe { ffi::AHardwareBuffer_getId(self.0, &mut out_id) };
+ assert_eq!(status, 0, "id() failed for AHardwareBuffer with error code: {status}");
+
+ out_id
+ }
+
+ /// Get the width of this buffer
+ pub fn width(&self) -> u32 {
+ self.description().width
+ }
+
+ /// Get the height of this buffer
+ pub fn height(&self) -> u32 {
+ self.description().height
+ }
+
+ /// Get the number of layers of this buffer
+ pub fn layers(&self) -> u32 {
+ self.description().layers
+ }
+
+ /// Get the format of this buffer
+ pub fn format(&self) -> AHardwareBuffer_Format::Type {
+ self.description().format
+ }
+
+ /// Get the usage bitvector of this buffer
+ pub fn usage(&self) -> AHardwareBuffer_UsageFlags {
+ AHardwareBuffer_UsageFlags(self.description().usage)
+ }
+
+ /// Get the stride of this buffer
+ pub fn stride(&self) -> u32 {
+ self.description().stride
+ }
+
+ fn description(&self) -> ffi::AHardwareBuffer_Desc {
+ let mut buffer_desc = ffi::AHardwareBuffer_Desc {
+ width: 0,
+ height: 0,
+ layers: 0,
+ format: 0,
+ usage: 0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
+ unsafe { ffi::AHardwareBuffer_describe(self.0, &mut buffer_desc) };
+ buffer_desc
+ }
+}
+
+impl Drop for AHardwareBuffer {
+ fn drop(&mut self) {
+ // SAFETY: self.0 will never be null. AHardwareBuffers allocated from within Rust will have
+ // a refcount of one, and there is a safety warning on taking an AHardwareBuffer from a raw
+ // pointer requiring callers to ensure the refcount is managed appropriately.
+ unsafe { ffi::AHardwareBuffer_release(self.0) }
+ }
+}
+
+#[cfg(test)]
+mod ahardwarebuffer_tests {
+ use super::*;
+
+ #[test]
+ fn create_valid_buffer_returns_ok() {
+ let buffer = AHardwareBuffer::new(
+ 512,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ );
+ assert!(buffer.is_some());
+ }
+
+ #[test]
+ fn create_invalid_buffer_returns_err() {
+ let buffer = AHardwareBuffer::new(512, 512, 1, 0, AHardwareBuffer_UsageFlags(0));
+ assert!(buffer.is_none());
+ }
+
+ #[test]
+ #[should_panic]
+ fn take_from_raw_panics_on_null() {
+ unsafe { AHardwareBuffer::take_from_raw(ptr::null_mut()) };
+ }
+
+ #[test]
+ fn take_from_raw_allows_getters() {
+ let buffer_desc = ffi::AHardwareBuffer_Desc {
+ width: 1024,
+ height: 512,
+ layers: 1,
+ format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN.0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ };
+ let mut raw_buffer_ptr = ptr::null_mut();
+
+ let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut raw_buffer_ptr) };
+ assert_eq!(status, 0);
+
+ let buffer = unsafe { AHardwareBuffer::take_from_raw(raw_buffer_ptr as *mut c_void) };
+ assert_eq!(buffer.width(), 1024);
+ }
+
+ #[test]
+ fn basic_getters() {
+ let buffer = AHardwareBuffer::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ )
+ .expect("Buffer with some basic parameters was not created successfully");
+
+ assert_eq!(buffer.width(), 1024);
+ assert_eq!(buffer.height(), 512);
+ assert_eq!(buffer.layers(), 1);
+ assert_eq!(buffer.format(), AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM);
+ assert_eq!(
+ buffer.usage(),
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN
+ );
+ }
+
+ #[test]
+ fn id_getter() {
+ let buffer = AHardwareBuffer::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ )
+ .expect("Buffer with some basic parameters was not created successfully");
+
+ assert_ne!(0, buffer.id());
+ }
+}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
new file mode 100644
index 0000000..e652aee
--- /dev/null
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/data_space.h>
+#include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
+#include <android/native_window.h>
diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl
index ed1b37d..b3fb7a7 100644
--- a/libs/permission/aidl/android/content/AttributionSourceState.aidl
+++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl
@@ -27,6 +27,10 @@
int pid = -1;
/** The UID that is accessing the permission protected data. */
int uid = -1;
+ /** The default device ID from where the permission protected data is read.
+ * @see Context#DEVICE_ID_DEFAULT
+ */
+ int deviceId = 0;
/** The package that is accessing the permission protected data. */
@nullable @utf8InCpp String packageName;
/** The attribution tag of the app accessing the permission protected data. */
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 23c99b0..dad3c19 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -20,6 +20,11 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <SkImage.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+
+#include <android/hardware_buffer.h>
#include "ColorSpaces.h"
#include "log/log_main.h"
#include "utils/Trace.h"
@@ -79,7 +84,7 @@
// releaseImageProc is invoked by SkImage, when the texture is no longer in use.
// "releaseContext" contains an "AutoBackendTexture*".
-void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) {
+void AutoBackendTexture::releaseImageProc(SkImages::ReleaseContext releaseContext) {
AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
textureRelease->unref(false);
}
@@ -136,8 +141,9 @@
}
sk_sp<SkImage> image =
- SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType,
- alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
+ SkImages::BorrowTextureFrom(context, mBackendTexture, kTopLeft_GrSurfaceOrigin,
+ colorType, alphaType, toSkColorSpace(dataspace),
+ releaseImageProc, this);
if (image.get()) {
// The following ref will be counteracted by releaseProc, when SkImage is discarded.
ref();
@@ -157,10 +163,10 @@
LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
if (!mSurface.get() || mDataspace != dataspace) {
sk_sp<SkSurface> surface =
- SkSurface::MakeFromBackendTexture(context, mBackendTexture,
- kTopLeft_GrSurfaceOrigin, 0, mColorType,
- toSkColorSpace(dataspace), nullptr,
- releaseSurfaceProc, this);
+ SkSurfaces::WrapBackendTexture(context, mBackendTexture,
+ kTopLeft_GrSurfaceOrigin, 0, mColorType,
+ toSkColorSpace(dataspace), nullptr,
+ releaseSurfaceProc, this);
if (surface.get()) {
// The following ref will be counteracted by releaseProc, when SkSurface is discarded.
ref();
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 00b901b..509ac40 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -144,7 +144,7 @@
CleanupManager& mCleanupMgr;
static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
- static void releaseImageProc(SkImage::ReleaseContext releaseContext);
+ static void releaseImageProc(SkImages::ReleaseContext releaseContext);
int mUsageCount = 0;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index d2228f1..29d8ba7 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,6 +20,7 @@
#include "SkiaRenderEngine.h"
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <GrBackendSemaphore.h>
#include <GrContextOptions.h>
#include <SkBlendMode.h>
@@ -1124,7 +1125,7 @@
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
- activeSurface->flush();
+ skgpu::ganesh::Flush(activeSurface);
}
}
for (const auto& borderRenderInfo : display.borderInfoList) {
@@ -1152,7 +1153,7 @@
{
ATRACE_NAME("flush surface");
LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
- activeSurface->flush();
+ skgpu::ganesh::Flush(activeSurface);
}
auto drawFence = sp<Fence>::make(flushAndSubmit(grContext));
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 2557ac9..1e0c4cf 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -16,6 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "BlurFilter.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkPaint.h>
#include <SkRRect.h>
@@ -23,6 +24,7 @@
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
+#include <SkTileMode.h>
#include <log/log.h>
#include <utils/Trace.h>
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index 511d7c9..e72c501 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -17,6 +17,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "GaussianBlurFilter.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkPaint.h>
#include <SkRRect.h>
@@ -25,6 +26,8 @@
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include "include/gpu/GpuTypes.h" // from Skia
#include <log/log.h>
#include <utils/Trace.h>
@@ -45,8 +48,8 @@
// Create blur surface with the bit depth and colorspace of the original surface
SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
std::ceil(blurRect.height() * kInputScale));
- sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context,
- skgpu::Budgeted::kNo, scaledInfo);
+ sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
+ skgpu::Budgeted::kNo, scaledInfo);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index e370c39..0c7335c 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -17,13 +17,20 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "KawaseBlurFilter.h"
+#include <SkAlphaType.h>
+#include <SkBlendMode.h>
#include <SkCanvas.h>
+#include <SkImageInfo.h>
#include <SkPaint.h>
#include <SkRRect.h>
#include <SkRuntimeEffect.h>
+#include <SkShader.h>
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -32,19 +39,18 @@
namespace skia {
KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
- SkString blurString(R"(
- uniform shader child;
- uniform float in_blurOffset;
+ SkString blurString(
+ "uniform shader child;"
+ "uniform float in_blurOffset;"
- half4 main(float2 xy) {
- half4 c = child.eval(xy);
- c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
- c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
- c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
- c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
- return half4(c.rgb * 0.2, 1.0);
- }
- )");
+ "half4 main(float2 xy) {"
+ "half4 c = child.eval(xy);"
+ "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));"
+ "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));"
+ "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));"
+ "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));"
+ "return half4(c.rgb * 0.2, 1.0);"
+ "}");
auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
if (!blurEffect) {
@@ -53,9 +59,44 @@
mBlurEffect = std::move(blurEffect);
}
-sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect)
- const {
+// Draws the given runtime shader on a GPU (Ganesh) surface and returns the result as an
+// SkImage.
+static sk_sp<SkImage> makeImage(GrRecordingContext* context, SkRuntimeShaderBuilder* builder,
+ const SkImageInfo& resultInfo) {
+ if (resultInfo.alphaType() == kUnpremul_SkAlphaType ||
+ resultInfo.alphaType() == kUnknown_SkAlphaType) {
+ return nullptr;
+ }
+ constexpr int kSampleCount = 1;
+ constexpr bool kMipmapped = false;
+
+ sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
+ skgpu::Budgeted::kYes,
+ resultInfo,
+ kSampleCount,
+ kTopLeft_GrSurfaceOrigin,
+ nullptr,
+ kMipmapped);
+ if (!surface) {
+ return nullptr;
+ }
+ sk_sp<SkShader> shader = builder->makeShader(nullptr);
+ if (!shader) {
+ return nullptr;
+ }
+ SkPaint paint;
+ paint.setShader(std::move(shader));
+ paint.setBlendMode(SkBlendMode::kSrc);
+ surface->getCanvas()->drawPaint(paint);
+ return surface->makeImageSnapshot();
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context,
+ const uint32_t blurRadius,
+ const sk_sp<SkImage> input,
+ const SkRect& blurRect) const {
+ LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__);
+ LOG_ALWAYS_FATAL_IF(input == nullptr, "%s: Invalid input image", __func__);
// Kawase is an approximation of Gaussian, but it behaves differently from it.
// A radius transformation is required for approximating them, and also to introduce
// non-integer steps, necessary to smoothly interpolate large radii.
@@ -80,14 +121,15 @@
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
- sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+ sk_sp<SkImage> tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
// And now we'll build our chain of scaled blur stages
for (auto i = 1; i < numberOfPasses; i++) {
+ LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i);
blurBuilder.child("child") =
tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
- tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
}
return tmpBlur;
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 019d6cb..634d35a 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -64,6 +64,14 @@
Sensor s;
Vector<Sensor> v;
uint32_t n = reply.readUint32();
+ // The size of the n Sensor elements on the wire is what we really want, but
+ // this is better than nothing.
+ if (n > reply.dataAvail()) {
+ ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+ "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+ n, reply.dataAvail());
+ return v;
+ }
v.setCapacity(n);
while (n) {
n--;
@@ -86,6 +94,14 @@
Sensor s;
Vector<Sensor> v;
uint32_t n = reply.readUint32();
+ // The size of the n Sensor elements on the wire is what we really want, but
+ // this is better than nothing.
+ if (n > reply.dataAvail()) {
+ ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+ "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+ n, reply.dataAvail());
+ return v;
+ }
v.setCapacity(n);
while (n) {
n--;
@@ -109,6 +125,14 @@
Sensor s;
Vector<Sensor> v;
uint32_t n = reply.readUint32();
+ // The size of the n Sensor elements on the wire is what we really want, but
+ // this is better than nothing.
+ if (n > reply.dataAvail()) {
+ ALOGE("Failed to get a reasonable size of the sensor list. This is likely a "
+ "malformed reply parcel. Number of elements: %d, data available in reply: %zu",
+ n, reply.dataAvail());
+ return v;
+ }
v.setCapacity(n);
while (n) {
n--;
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index 2be98e7..57c74ee 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -32,27 +32,12 @@
sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService()
{
std::lock_guard<Mutex> scoped_lock(mLock);
- int64_t startTime = 0;
sp<hardware::ISensorPrivacyManager> service = mService;
- while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
- sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy"));
- if (binder == nullptr) {
- // Wait for the sensor privacy service to come back...
- if (startTime == 0) {
- startTime = uptimeMillis();
- ALOGI("Waiting for sensor privacy service");
- } else if ((uptimeMillis() - startTime) > 1000000) {
- ALOGW("Waiting too long for sensor privacy service, giving up");
- service = nullptr;
- break;
- }
- usleep(25000);
- } else {
- service = interface_cast<hardware::ISensorPrivacyManager>(binder);
- mService = service;
- }
+ if (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->waitForService(String16("sensor_privacy"));
+ mService = interface_cast<hardware::ISensorPrivacyManager>(binder);
}
- return service;
+ return mService;
}
bool SensorPrivacyManager::supportsSensorToggle(int toggleType, int sensor) {
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c3b2d3d..37ebfc4 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -31,10 +31,15 @@
using namespace aidl::android::hardware::graphics::common;
using namespace ::android::hardware::graphics::mapper;
+using ADataspace = aidl::android::hardware::graphics::common::Dataspace;
+using APixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
namespace android {
static const auto kIAllocatorServiceName = IAllocator::descriptor + std::string("/default");
static const auto kIAllocatorMinimumVersion = 2;
+constexpr const char* kStandardMetadataName =
+ "android.hardware.graphics.common.StandardMetadataType";
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13));
@@ -284,17 +289,205 @@
return mMapper != nullptr && mMapper->version >= AIMAPPER_VERSION_5;
}
+static bool isStandardMetadata(AIMapper_MetadataType metadataType) {
+ return strcmp(kStandardMetadataName, metadataType.name) == 0;
+}
+
+struct DumpBufferResult {
+ uint64_t bufferId;
+ std::string name;
+ uint64_t width;
+ uint64_t height;
+ uint64_t layerCount;
+ APixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ uint64_t pixelFormatModifier;
+ BufferUsage usage;
+ ADataspace dataspace;
+ uint64_t allocationSize;
+ uint64_t protectedContent;
+ ExtendableType compression;
+ ExtendableType interlaced;
+ ExtendableType chromaSiting;
+ std::vector<ui::PlaneLayout> planeLayouts;
+};
+
+#define DECODE_TO(name, output) \
+ case StandardMetadataType::name: \
+ output = StandardMetadata<StandardMetadataType::name>::value ::decode(value, valueSize) \
+ .value(); \
+ break
+
+static void dumpBufferCommon(DumpBufferResult* outResult, AIMapper_MetadataType metadataType,
+ const void* value, size_t valueSize) {
+ if (!isStandardMetadata(metadataType)) {
+ return;
+ }
+ StandardMetadataType type = (StandardMetadataType)metadataType.value;
+ switch (type) {
+ DECODE_TO(BUFFER_ID, outResult->bufferId);
+ DECODE_TO(NAME, outResult->name);
+ DECODE_TO(WIDTH, outResult->width);
+ DECODE_TO(HEIGHT, outResult->height);
+ DECODE_TO(LAYER_COUNT, outResult->layerCount);
+ DECODE_TO(PIXEL_FORMAT_REQUESTED, outResult->pixelFormatRequested);
+ DECODE_TO(PIXEL_FORMAT_FOURCC, outResult->pixelFormatFourCC);
+ DECODE_TO(PIXEL_FORMAT_MODIFIER, outResult->pixelFormatModifier);
+ DECODE_TO(USAGE, outResult->usage);
+ DECODE_TO(DATASPACE, outResult->dataspace);
+ DECODE_TO(ALLOCATION_SIZE, outResult->allocationSize);
+ DECODE_TO(PROTECTED_CONTENT, outResult->protectedContent);
+ DECODE_TO(COMPRESSION, outResult->compression);
+ DECODE_TO(INTERLACED, outResult->interlaced);
+ DECODE_TO(CHROMA_SITING, outResult->chromaSiting);
+ DECODE_TO(PLANE_LAYOUTS, outResult->planeLayouts);
+ default:
+ break;
+ }
+}
+
+#undef DECODE_TO
+
+template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>{}>>
+constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept {
+ return static_cast<std::underlying_type_t<EnumT>>(e);
+}
+
+static void writeDumpToStream(const DumpBufferResult& bufferDump, std::ostream& outDump,
+ bool less) {
+ double allocationSizeKiB = static_cast<double>(bufferDump.allocationSize) / 1024;
+
+ outDump << "+ name:" << bufferDump.name << ", id:" << bufferDump.bufferId
+ << ", size:" << std::fixed << allocationSizeKiB << "KiB, w/h:" << bufferDump.width
+ << "x" << bufferDump.height << ", usage: 0x" << std::hex
+ << to_underlying(bufferDump.usage) << std::dec
+ << ", req fmt:" << to_underlying(bufferDump.pixelFormatRequested)
+ << ", fourcc/mod:" << bufferDump.pixelFormatFourCC << "/"
+ << bufferDump.pixelFormatModifier << ", dataspace: 0x" << std::hex
+ << to_underlying(bufferDump.dataspace) << std::dec << ", compressed: ";
+
+ if (less) {
+ bool isCompressed = !gralloc4::isStandardCompression(bufferDump.compression) ||
+ (gralloc4::getStandardCompressionValue(bufferDump.compression) !=
+ ui::Compression::NONE);
+ outDump << std::boolalpha << isCompressed << "\n";
+ } else {
+ outDump << gralloc4::getCompressionName(bufferDump.compression) << "\n";
+ }
+
+ if (!less) {
+ bool firstPlane = true;
+ for (const auto& planeLayout : bufferDump.planeLayouts) {
+ if (firstPlane) {
+ firstPlane = false;
+ outDump << "\tplanes: ";
+ } else {
+ outDump << "\t ";
+ }
+
+ for (size_t i = 0; i < planeLayout.components.size(); i++) {
+ const auto& planeLayoutComponent = planeLayout.components[i];
+ outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+ if (i < planeLayout.components.size() - 1) {
+ outDump << "/";
+ } else {
+ outDump << ":\t";
+ }
+ }
+ outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+ << ", stride:" << planeLayout.strideInBytes
+ << " bytes, size:" << planeLayout.totalSizeInBytes;
+ outDump << ", inc:" << planeLayout.sampleIncrementInBits
+ << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+ << planeLayout.verticalSubsampling;
+ outDump << "\n";
+ }
+
+ outDump << "\tlayer cnt: " << bufferDump.layerCount
+ << ", protected content: " << bufferDump.protectedContent
+ << ", interlaced: " << gralloc4::getInterlacedName(bufferDump.interlaced)
+ << ", chroma siting:" << gralloc4::getChromaSitingName(bufferDump.chromaSiting)
+ << "\n";
+ }
+}
+
std::string Gralloc5Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
- // TODO(b/261858392): Implement
- (void)bufferHandle;
- (void)less;
- return {};
+ DumpBufferResult bufferInfo;
+ AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
+ AIMapper_MetadataType metadataType,
+ const void* _Nonnull value, size_t valueSize) {
+ DumpBufferResult* context = reinterpret_cast<DumpBufferResult*>(contextPtr);
+ dumpBufferCommon(context, metadataType, value, valueSize);
+ };
+ AIMapper_Error error = mMapper->v5.dumpBuffer(bufferHandle, dumpBuffer, &bufferInfo);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("Error dumping buffer: %d", error);
+ return std::string{};
+ }
+ std::ostringstream stream;
+ stream.precision(2);
+ writeDumpToStream(bufferInfo, stream, less);
+ return stream.str();
}
std::string Gralloc5Mapper::dumpBuffers(bool less) const {
- // TODO(b/261858392): Implement
- (void)less;
- return {};
+ class DumpAllBuffersContext {
+ private:
+ bool mHasPending = false;
+ DumpBufferResult mPending;
+ std::vector<DumpBufferResult> mResults;
+
+ public:
+ DumpAllBuffersContext() { mResults.reserve(10); }
+
+ void commit() {
+ if (mHasPending) {
+ mResults.push_back(mPending);
+ mHasPending = false;
+ }
+ }
+
+ DumpBufferResult* write() {
+ mHasPending = true;
+ return &mPending;
+ }
+
+ const std::vector<DumpBufferResult>& results() {
+ commit();
+ return mResults;
+ }
+ } context;
+
+ AIMapper_BeginDumpBufferCallback beginCallback = [](void* contextPtr) {
+ DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
+ context->commit();
+ };
+
+ AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
+ AIMapper_MetadataType metadataType,
+ const void* _Nonnull value, size_t valueSize) {
+ DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
+ dumpBufferCommon(context->write(), metadataType, value, valueSize);
+ };
+
+ AIMapper_Error error = mMapper->v5.dumpAllBuffers(beginCallback, dumpBuffer, &context);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("Error dumping buffers: %d", error);
+ return std::string{};
+ }
+ uint64_t totalAllocationSize = 0;
+ std::ostringstream stream;
+ stream.precision(2);
+ stream << "Imported gralloc buffers:\n";
+
+ for (const auto& bufferDump : context.results()) {
+ writeDumpToStream(bufferDump, stream, less);
+ totalAllocationSize += bufferDump.allocationSize;
+ }
+
+ double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+ stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+ return stream.str();
}
status_t Gralloc5Mapper::importBuffer(const native_handle_t *rawHandle,
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index c0abec2..eb0bd4e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -89,14 +89,14 @@
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
const size_t count = list.size();
- StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ StringAppendF(&result, "%14s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
"W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
std::string sizeStr = (rec.size)
? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
: "unknown";
- StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ StringAppendF(&result, "%14p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index dbe475b..f859848 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -36,6 +36,15 @@
#include <hardware/gralloc.h>
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+// TODO: Provide alternatives that aren't broken
+#define AHB_CONVERSION \
+ [[deprecated("WARNING: VNDK casts beteween GraphicBuffer & AHardwareBuffer are UNSAFE and " \
+ "will be removed in the future")]]
+#else
+#define AHB_CONVERSION
+#endif
+
namespace android {
class GraphicBufferMapper;
@@ -80,10 +89,10 @@
static sp<GraphicBuffer> from(ANativeWindowBuffer *);
- static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*);
- static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*);
- AHardwareBuffer* toAHardwareBuffer();
- AHardwareBuffer const* toAHardwareBuffer() const;
+ AHB_CONVERSION static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*);
+ AHB_CONVERSION static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*);
+ AHB_CONVERSION AHardwareBuffer* toAHardwareBuffer();
+ AHB_CONVERSION AHardwareBuffer const* toAHardwareBuffer() const;
// Create a GraphicBuffer to be unflatten'ed into or be reallocated.
GraphicBuffer();
diff --git a/libs/ultrahdr/gainmapmath.cpp b/libs/ultrahdr/gainmapmath.cpp
index ee15363..8015a4e 100644
--- a/libs/ultrahdr/gainmapmath.cpp
+++ b/libs/ultrahdr/gainmapmath.cpp
@@ -547,13 +547,13 @@
uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx];
uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx];
- y1_uint = static_cast<uint8_t>(floor(yuv1.y * 255.0f + 0.5f));
- y2_uint = static_cast<uint8_t>(floor(yuv2.y * 255.0f + 0.5f));
- y3_uint = static_cast<uint8_t>(floor(yuv3.y * 255.0f + 0.5f));
- y4_uint = static_cast<uint8_t>(floor(yuv4.y * 255.0f + 0.5f));
+ y1_uint = static_cast<uint8_t>(CLIP3((yuv1.y * 255.0f + 0.5f), 0, 255));
+ y2_uint = static_cast<uint8_t>(CLIP3((yuv2.y * 255.0f + 0.5f), 0, 255));
+ y3_uint = static_cast<uint8_t>(CLIP3((yuv3.y * 255.0f + 0.5f), 0, 255));
+ y4_uint = static_cast<uint8_t>(CLIP3((yuv4.y * 255.0f + 0.5f), 0, 255));
- u_uint = static_cast<uint8_t>(floor(new_uv.u * 255.0f + 128.0f + 0.5f));
- v_uint = static_cast<uint8_t>(floor(new_uv.v * 255.0f + 128.0f + 0.5f));
+ u_uint = static_cast<uint8_t>(CLIP3((new_uv.u * 255.0f + 128.0f + 0.5f), 0, 255));
+ v_uint = static_cast<uint8_t>(CLIP3((new_uv.v * 255.0f + 128.0f + 0.5f), 0, 255));
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp
index 1ab3c7c..e41b645 100644
--- a/libs/ultrahdr/icc.cpp
+++ b/libs/ultrahdr/icc.cpp
@@ -19,7 +19,6 @@
#endif
#include <ultrahdr/icc.h>
-#include <ultrahdr/gainmapmath.h>
#include <vector>
#include <utils/Log.h>
@@ -218,8 +217,8 @@
total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
for (size_t i = 0; i < table_entries; ++i) {
uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
dataStruct->write16(value);
@@ -227,14 +226,30 @@
return dataStruct;
}
-sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
- int total_length = 16;
- sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0)));
+sp<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
+ if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f
+ && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
+ int total_length = 16;
+ sp<DataStruct> dataStruct = new DataStruct(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+ return dataStruct;
+ }
+ int total_length = 40;
+ sp<DataStruct> dataStruct = new DataStruct(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
return dataStruct;
}
@@ -349,7 +364,7 @@
// The "B" curve is required.
for (size_t i = 0; i < kNumChannels; ++i) {
- b_curves_data[i] = write_trc_tag_for_linear();
+ b_curves_data[i] = write_trc_tag(kLinear_TransFun);
}
// The "A" curve and CLUT are optional.
@@ -362,7 +377,7 @@
a_curves_offset = clut_offset + clut->getLength();
for (size_t i = 0; i < kNumChannels; ++i) {
- a_curves_data[i] = write_trc_tag_for_linear();
+ a_curves_data[i] = write_trc_tag(kLinear_TransFun);
}
}
@@ -460,9 +475,9 @@
tags.emplace_back(kTAG_bTRC,
write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
} else {
- tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear());
- tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear());
- tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear());
+ tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
+ tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
+ tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
}
}
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
index edf152d..50b4d2f 100644
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -51,6 +51,23 @@
typedef Color (*ColorTransformFn)(Color);
typedef float (*ColorCalculationFn)(Color);
+// A transfer function mapping encoded values to linear values,
+// represented by this 7-parameter piecewise function:
+//
+// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
+// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
+//
+// (A simple gamma transfer function sets g to gamma and a to 1.)
+typedef struct TransferFunction {
+ float g, a,b,c,d,e,f;
+} TransferFunction;
+
+static constexpr TransferFunction kSRGB_TransFun =
+ { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
+
+static constexpr TransferFunction kLinear_TransFun =
+ { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
inline Color operator+=(Color& lhs, const Color& rhs) {
lhs.r += rhs.r;
lhs.g += rhs.g;
@@ -312,7 +329,7 @@
float hlgOetfLUT(float e);
Color hlgOetfLUT(Color e);
-constexpr size_t kHlgOETFPrecision = 10;
+constexpr size_t kHlgOETFPrecision = 16;
constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
/*
@@ -325,7 +342,7 @@
float hlgInvOetfLUT(float e_gamma);
Color hlgInvOetfLUT(Color e_gamma);
-constexpr size_t kHlgInvOETFPrecision = 10;
+constexpr size_t kHlgInvOETFPrecision = 12;
constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
/*
@@ -338,7 +355,7 @@
float pqOetfLUT(float e);
Color pqOetfLUT(Color e);
-constexpr size_t kPqOETFPrecision = 10;
+constexpr size_t kPqOETFPrecision = 16;
constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
/*
@@ -351,7 +368,7 @@
float pqInvOetfLUT(float e_gamma);
Color pqInvOetfLUT(Color e_gamma);
-constexpr size_t kPqInvOETFPrecision = 10;
+constexpr size_t kPqInvOETFPrecision = 12;
constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
diff --git a/libs/ultrahdr/include/ultrahdr/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h
index 7f047f8..971b267 100644
--- a/libs/ultrahdr/include/ultrahdr/icc.h
+++ b/libs/ultrahdr/include/ultrahdr/icc.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_ULTRAHDR_ICC_H
#define ANDROID_ULTRAHDR_ICC_H
+#include <ultrahdr/gainmapmath.h>
#include <ultrahdr/jpegr.h>
#include <ultrahdr/jpegrutils.h>
#include <utils/RefBase.h>
@@ -222,7 +223,7 @@
const ultrahdr_color_gamut gamut);
static sp<DataStruct> write_xyz_tag(float x, float y, float z);
static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
- static sp<DataStruct> write_trc_tag_for_linear();
+ static sp<DataStruct> write_trc_tag(const TransferFunction& fn);
static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
uint32_t transfer_characteristics);
diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
index a35fd30..f80496a 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegr.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegr.h
@@ -348,16 +348,6 @@
status_t extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image,
jr_compressed_ptr primary_image,
jr_compressed_ptr gain_map);
- /*
- * This method is called in the decoding pipeline. It will read XMP metadata to find the start
- * position of the compressed gain map, and will extract the compressed gain map.
- *
- * @param compressed_jpegr_image compressed JPEGR image
- * @param dest destination of compressed gain map
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t extractGainMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest);
/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
index 0641232..5420e1c 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
@@ -44,6 +44,7 @@
ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 6,
ERROR_JPEGR_INVALID_METADATA = JPEGR_IO_ERROR_BASE - 7,
ERROR_JPEGR_UNSUPPORTED_METADATA = JPEGR_IO_ERROR_BASE - 8,
+ ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_IO_ERROR_BASE - 9,
JPEGR_RUNTIME_ERROR_BASE = -20000,
ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 9c57f34..5a601bd 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -539,9 +539,12 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- jpegr_compressed_struct primary_image, gain_map;
- JPEGR_CHECK(extractPrimaryImageAndGainMap(compressed_jpegr_image,
- &primary_image, &gain_map));
+ jpegr_compressed_struct primary_image, gainmap_image;
+ status_t status =
+ extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+ if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+ return status;
+ }
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
@@ -550,7 +553,7 @@
return ERROR_JPEGR_DECODE_ERROR;
}
- return NO_ERROR;
+ return status;
}
/* Decode API */
@@ -586,90 +589,34 @@
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- if (output_format == ULTRAHDR_OUTPUT_SDR) {
- JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
- true)) {
- return ERROR_JPEGR_DECODE_ERROR;
+ jpegr_compressed_struct primary_image, gainmap_image;
+ status_t status =
+ extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+ if (status != NO_ERROR) {
+ if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+ ALOGE("received invalid compressed jpegr image");
+ return status;
}
- jpegr_uncompressed_struct uncompressed_rgba_image;
- uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
- uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
- uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
- memcpy(dest->data, uncompressed_rgba_image.data,
- uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
- dest->width = uncompressed_rgba_image.width;
- dest->height = uncompressed_rgba_image.height;
-
- if (gain_map == nullptr && exif == nullptr) {
- return NO_ERROR;
- }
-
- if (exif != nullptr) {
- if (exif->data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (exif->length < jpeg_decoder.getEXIFSize()) {
- return ERROR_JPEGR_BUFFER_TOO_SMALL;
- }
- memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
- exif->length = jpeg_decoder.getEXIFSize();
- }
- if (gain_map == nullptr) {
- return NO_ERROR;
- }
- }
-
- jpegr_compressed_struct compressed_map;
- JPEGR_CHECK(extractGainMap(compressed_jpegr_image, &compressed_map));
-
- JpegDecoderHelper gain_map_decoder;
- if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
- if ((gain_map_decoder.getDecompressedImageWidth() *
- gain_map_decoder.getDecompressedImageHeight()) >
- gain_map_decoder.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
- }
-
- if (gain_map != nullptr) {
- gain_map->width = gain_map_decoder.getDecompressedImageWidth();
- gain_map->height = gain_map_decoder.getDecompressedImageHeight();
- int size = gain_map->width * gain_map->height;
- gain_map->data = malloc(size);
- memcpy(gain_map->data, gain_map_decoder.getDecompressedImagePtr(), size);
- }
-
- ultrahdr_metadata_struct uhdr_metadata;
- if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
- gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
- return ERROR_JPEGR_INVALID_METADATA;
- }
-
- if (metadata != nullptr) {
- metadata->version = uhdr_metadata.version;
- metadata->minContentBoost = uhdr_metadata.minContentBoost;
- metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
- metadata->gamma = uhdr_metadata.gamma;
- metadata->offsetSdr = uhdr_metadata.offsetSdr;
- metadata->offsetHdr = uhdr_metadata.offsetHdr;
- metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
- metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
- }
-
- if (output_format == ULTRAHDR_OUTPUT_SDR) {
- return NO_ERROR;
}
JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
+ if (!jpeg_decoder.decompressImage(primary_image.data, primary_image.length,
+ (output_format == ULTRAHDR_OUTPUT_SDR))) {
return ERROR_JPEGR_DECODE_ERROR;
}
- if ((jpeg_decoder.getDecompressedImageWidth() *
- jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
- jpeg_decoder.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
+
+ if (output_format == ULTRAHDR_OUTPUT_SDR) {
+ if ((jpeg_decoder.getDecompressedImageWidth() *
+ jpeg_decoder.getDecompressedImageHeight() * 4) >
+ jpeg_decoder.getDecompressedImageSize()) {
+ return ERROR_JPEGR_CALCULATION_ERROR;
+ }
+ } else {
+ if ((jpeg_decoder.getDecompressedImageWidth() *
+ jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
+ jpeg_decoder.getDecompressedImageSize()) {
+ return ERROR_JPEGR_CALCULATION_ERROR;
+ }
}
if (exif != nullptr) {
@@ -683,11 +630,53 @@
exif->length = jpeg_decoder.getEXIFSize();
}
+ if (output_format == ULTRAHDR_OUTPUT_SDR) {
+ dest->width = jpeg_decoder.getDecompressedImageWidth();
+ dest->height = jpeg_decoder.getDecompressedImageHeight();
+ memcpy(dest->data, jpeg_decoder.getDecompressedImagePtr(), dest->width * dest->height * 4);
+ return NO_ERROR;
+ }
+
+ JpegDecoderHelper gain_map_decoder;
+ if (!gain_map_decoder.decompressImage(gainmap_image.data, gainmap_image.length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+ if ((gain_map_decoder.getDecompressedImageWidth() *
+ gain_map_decoder.getDecompressedImageHeight()) >
+ gain_map_decoder.getDecompressedImageSize()) {
+ return ERROR_JPEGR_CALCULATION_ERROR;
+ }
+
jpegr_uncompressed_struct map;
map.data = gain_map_decoder.getDecompressedImagePtr();
map.width = gain_map_decoder.getDecompressedImageWidth();
map.height = gain_map_decoder.getDecompressedImageHeight();
+ if (gain_map != nullptr) {
+ gain_map->width = map.width;
+ gain_map->height = map.height;
+ int size = gain_map->width * gain_map->height;
+ gain_map->data = malloc(size);
+ memcpy(gain_map->data, map.data, size);
+ }
+
+ ultrahdr_metadata_struct uhdr_metadata;
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
+ gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
+ return ERROR_JPEGR_INVALID_METADATA;
+ }
+
+ if (metadata != nullptr) {
+ metadata->version = uhdr_metadata.version;
+ metadata->minContentBoost = uhdr_metadata.minContentBoost;
+ metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
+ metadata->gamma = uhdr_metadata.gamma;
+ metadata->offsetSdr = uhdr_metadata.offsetSdr;
+ metadata->offsetHdr = uhdr_metadata.offsetHdr;
+ metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
+ metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
+ }
+
jpegr_uncompressed_struct uncompressed_yuv_420_image;
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
@@ -1070,7 +1059,7 @@
}
case ULTRAHDR_OUTPUT_HDR_PQ:
{
-#if USE_HLG_OETF_LUT
+#if USE_PQ_OETF_LUT
ColorTransformFn hdrOetf = pqOetfLUT;
#else
ColorTransformFn hdrOetf = pqOetf;
@@ -1131,12 +1120,8 @@
const auto& jpeg_info = jpeg_info_builder.GetInfo();
const auto& image_ranges = jpeg_info.GetImageRanges();
- if (image_ranges.empty()) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (image_ranges.size() != 2) {
- // Must be 2 JPEG Images
+ if (image_ranges.empty()) {
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
@@ -1146,23 +1131,23 @@
primary_image->length = image_ranges[0].GetLength();
}
+ if (image_ranges.size() == 1) {
+ return ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND;
+ }
+
if (gain_map != nullptr) {
gain_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
image_ranges[1].GetBegin();
gain_map->length = image_ranges[1].GetLength();
}
- return NO_ERROR;
-}
-
-
-status_t JpegR::extractGainMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest) {
- if (compressed_jpegr_image == nullptr || dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ // TODO: choose primary image and gain map image carefully
+ if (image_ranges.size() > 2) {
+ ALOGW("Number of jpeg images present %d, primary, gain map images may not be correctly chosen",
+ (int)image_ranges.size());
}
- return extractPrimaryImageAndGainMap(compressed_jpegr_image, nullptr, dest);
+ return NO_ERROR;
}
// JPEG/R structure:
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
index 5944130..004a582 100644
--- a/libs/ultrahdr/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -22,7 +22,7 @@
}
cc_test {
- name: "libultrahdr_test",
+ name: "ultrahdr_unit_test",
test_suites: ["device-tests"],
srcs: [
"gainmapmath_test.cpp",
@@ -45,7 +45,7 @@
}
cc_test {
- name: "libjpegencoderhelper_test",
+ name: "jpegencoderhelper_test",
test_suites: ["device-tests"],
srcs: [
"jpegencoderhelper_test.cpp",
@@ -61,7 +61,7 @@
}
cc_test {
- name: "libjpegdecoderhelper_test",
+ name: "jpegdecoderhelper_test",
test_suites: ["device-tests"],
srcs: [
"jpegdecoderhelper_test.cpp",
diff --git a/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010 b/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
deleted file mode 100644
index e7a5dc8..0000000
--- a/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index 41d55ec..7837bcf 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -14,810 +14,1232 @@
* limitations under the License.
*/
+#include <sys/time.h>
+#include <fstream>
+#include <iostream>
+
+#include <ultrahdr/gainmapmath.h>
#include <ultrahdr/jpegr.h>
#include <ultrahdr/jpegrutils.h>
-#include <ultrahdr/gainmapmath.h>
-#include <fcntl.h>
-#include <fstream>
+
#include <gtest/gtest.h>
-#include <sys/time.h>
#include <utils/Log.h>
-#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
-#define RAW_P010_IMAGE_WITH_STRIDE "/sdcard/Documents/raw_p010_image_with_stride.p010"
-#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
-#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
-#define TEST_IMAGE_WIDTH 1280
-#define TEST_IMAGE_HEIGHT 720
-#define TEST_IMAGE_STRIDE 1288
-#define DEFAULT_JPEG_QUALITY 90
-
-#define SAVE_ENCODING_RESULT true
-#define SAVE_DECODING_RESULT true
-#define SAVE_INPUT_RGBA true
+//#define DUMP_OUTPUT
namespace android::ultrahdr {
-struct Timer {
- struct timeval StartingTime;
- struct timeval EndingTime;
- struct timeval ElapsedMicroseconds;
-};
+// resources used by unit tests
+const char* kYCbCrP010FileName = "raw_p010_image.p010";
+const char* kYCbCr420FileName = "raw_yuv420_image.yuv420";
+const char* kSdrJpgFileName = "jpeg_image.jpg";
+const int kImageWidth = 1280;
+const int kImageHeight = 720;
+const int kQuality = 90;
-void timerStart(Timer *t) {
- gettimeofday(&t->StartingTime, nullptr);
-}
+// Wrapper to describe the input type
+typedef enum {
+ YCbCr_p010 = 0,
+ YCbCr_420 = 1,
+} UhdrInputFormat;
-void timerStop(Timer *t) {
- gettimeofday(&t->EndingTime, nullptr);
-}
-
-int64_t elapsedTime(Timer *t) {
- t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec;
- t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec;
- return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec;
-}
-
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
-}
-
-static bool loadFile(const char filename[], void*& result, int* fileLength) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
- return false;
- }
- if (fileLength != nullptr) {
- *fileLength = length;
- }
- result = malloc(length);
- if (read(fd, result, length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
- }
- close(fd);
- return true;
-}
-
-static bool loadP010Image(const char *filename, jr_uncompressed_ptr img,
- bool isUVContiguous) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- const int bpp = 2;
- int lumaStride = img->luma_stride == 0 ? img->width : img->luma_stride;
- int lumaSize = bpp * lumaStride * img->height;
- int chromaSize = bpp * (img->height / 2) *
- (isUVContiguous ? lumaStride : img->chroma_stride);
- img->data = malloc(lumaSize + (isUVContiguous ? chromaSize : 0));
- if (img->data == nullptr) {
- ALOGE("loadP010Image(): failed to allocate memory for luma data.");
- return false;
- }
- uint8_t *mem = static_cast<uint8_t *>(img->data);
- for (int i = 0; i < img->height; i++) {
- if (read(fd, mem, img->width * bpp) != img->width * bpp) {
- close(fd);
- return false;
- }
- mem += lumaStride * bpp;
- }
- int chromaStride = lumaStride;
- if (!isUVContiguous) {
- img->chroma_data = malloc(chromaSize);
- if (img->chroma_data == nullptr) {
- ALOGE("loadP010Image(): failed to allocate memory for chroma data.");
- return false;
- }
- mem = static_cast<uint8_t *>(img->chroma_data);
- chromaStride = img->chroma_stride;
- }
- for (int i = 0; i < img->height / 2; i++) {
- if (read(fd, mem, img->width * bpp) != img->width * bpp) {
- close(fd);
- return false;
- }
- mem += chromaStride * bpp;
- }
- close(fd);
- return true;
-}
-
-class JpegRTest : public testing::Test {
+/**
+ * Wrapper class for raw resource
+ * Sample usage:
+ * UhdrUnCompressedStructWrapper rawImg(width, height, YCbCr_p010);
+ * rawImg.setImageColorGamut(colorGamut));
+ * rawImg.setImageStride(strideLuma, strideChroma); // optional
+ * rawImg.setChromaMode(false); // optional
+ * rawImg.allocateMemory();
+ * rawImg.loadRawResource(kYCbCrP010FileName);
+ */
+class UhdrUnCompressedStructWrapper {
public:
- JpegRTest();
- ~JpegRTest();
+ UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height, UhdrInputFormat format);
+ ~UhdrUnCompressedStructWrapper() = default;
-protected:
- virtual void SetUp();
- virtual void TearDown();
+ bool setChromaMode(bool isChromaContiguous);
+ bool setImageStride(int lumaStride, int chromaStride);
+ bool setImageColorGamut(ultrahdr_color_gamut colorGamut);
+ bool allocateMemory();
+ bool loadRawResource(const char* fileName);
+ jr_uncompressed_ptr getImageHandle();
- struct jpegr_uncompressed_struct mRawP010Image{};
- struct jpegr_uncompressed_struct mRawP010ImageWithStride{};
- struct jpegr_uncompressed_struct mRawP010ImageWithChromaData{};
- struct jpegr_uncompressed_struct mRawYuv420Image{};
- struct jpegr_compressed_struct mJpegImage{};
-};
-
-JpegRTest::JpegRTest() {}
-JpegRTest::~JpegRTest() {}
-
-void JpegRTest::SetUp() {}
-void JpegRTest::TearDown() {
- free(mRawP010Image.data);
- free(mRawP010Image.chroma_data);
- free(mRawP010ImageWithStride.data);
- free(mRawP010ImageWithStride.chroma_data);
- free(mRawP010ImageWithChromaData.data);
- free(mRawP010ImageWithChromaData.chroma_data);
- free(mRawYuv420Image.data);
- free(mJpegImage.data);
-}
-
-class JpegRBenchmark : public JpegR {
-public:
- void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
- void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
private:
- const int kProfileCount = 10;
+ std::unique_ptr<uint8_t[]> mLumaData;
+ std::unique_ptr<uint8_t[]> mChromaData;
+ jpegr_uncompressed_struct mImg;
+ UhdrInputFormat mFormat;
+ bool mIsChromaContiguous;
};
-void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
- jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr map) {
- ASSERT_EQ(yuv420Image->width, p010Image->width);
- ASSERT_EQ(yuv420Image->height, p010Image->height);
+/**
+ * Wrapper class for compressed resource
+ * Sample usage:
+ * UhdrCompressedStructWrapper jpgImg(width, height);
+ * rawImg.allocateMemory();
+ */
+class UhdrCompressedStructWrapper {
+public:
+ UhdrCompressedStructWrapper(uint32_t width, uint32_t height);
+ ~UhdrCompressedStructWrapper() = default;
- Timer genRecMapTime;
+ bool allocateMemory();
+ jr_compressed_ptr getImageHandle();
- timerStart(&genRecMapTime);
- for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, generateGainMap(
- yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, metadata, map));
- if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data);
+private:
+ std::unique_ptr<uint8_t[]> mData;
+ jpegr_compressed_struct mImg{};
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
+
+UhdrUnCompressedStructWrapper::UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height,
+ UhdrInputFormat format) {
+ mImg.data = nullptr;
+ mImg.width = width;
+ mImg.height = height;
+ mImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ mImg.chroma_data = nullptr;
+ mImg.luma_stride = 0;
+ mImg.chroma_stride = 0;
+ mFormat = format;
+ mIsChromaContiguous = true;
+}
+
+bool UhdrUnCompressedStructWrapper::setChromaMode(bool isChromaContiguous) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
+ return false;
}
- timerStop(&genRecMapTime);
-
- ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms",
- yuv420Image->width, yuv420Image->height,
- elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f));
-
+ mIsChromaContiguous = isChromaContiguous;
+ return true;
}
-void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image,
- jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
- Timer applyRecMapTime;
-
- timerStart(&applyRecMapTime);
- for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
- metadata->maxContentBoost /* displayBoost */, dest));
+bool UhdrUnCompressedStructWrapper::setImageStride(int lumaStride, int chromaStride) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
+ return false;
}
- timerStop(&applyRecMapTime);
-
- ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms",
- yuv420Image->width, yuv420Image->height,
- elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f));
+ if (lumaStride != 0) {
+ if (lumaStride < mImg.width) {
+ std::cerr << "Bad luma stride received" << std::endl;
+ return false;
+ }
+ mImg.luma_stride = lumaStride;
+ }
+ if (chromaStride != 0) {
+ if (mFormat == YCbCr_p010 && chromaStride < mImg.width) {
+ std::cerr << "Bad chroma stride received for format YCbCrP010" << std::endl;
+ return false;
+ }
+ if (mFormat == YCbCr_420 && chromaStride < (mImg.width >> 1)) {
+ std::cerr << "Bad chroma stride received for format YCbCr420" << std::endl;
+ return false;
+ }
+ mImg.chroma_stride = chromaStride;
+ }
+ return true;
}
-TEST_F(JpegRTest, build) {
- // Force all of the gain map lib to be linked by calling all public functions.
- JpegR jpegRCodec;
- jpegRCodec.encodeJPEGR(nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr, 0, nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
- nullptr, 0, nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
- nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr);
- jpegRCodec.decodeJPEGR(nullptr, nullptr);
+bool UhdrUnCompressedStructWrapper::setImageColorGamut(ultrahdr_color_gamut colorGamut) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
+ return false;
+ }
+ mImg.colorGamut = colorGamut;
+ return true;
}
-/* Test Encode API-0 invalid arguments */
-TEST_F(JpegRTest, encodeAPI0ForInvalidArgs) {
- int ret;
+bool UhdrUnCompressedStructWrapper::allocateMemory() {
+ if (mImg.width == 0 || (mImg.width % 2 != 0) || mImg.height == 0 || (mImg.height % 2 != 0) ||
+ (mFormat != YCbCr_p010 && mFormat != YCbCr_420)) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
+ }
+ int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
+ int lumaSize = lumaStride * mImg.height * (mFormat == YCbCr_p010 ? 2 : 1);
+ int chromaSize = (mImg.height >> 1) * (mFormat == YCbCr_p010 ? 2 : 1);
+ if (mIsChromaContiguous) {
+ chromaSize *= lumaStride;
+ } else {
+ if (mImg.chroma_stride == 0) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
+ }
+ if (mFormat == YCbCr_p010) {
+ chromaSize *= mImg.chroma_stride;
+ } else {
+ chromaSize *= (mImg.chroma_stride * 2);
+ }
+ }
+ if (mIsChromaContiguous) {
+ mLumaData = std::make_unique<uint8_t[]>(lumaSize + chromaSize);
+ mImg.data = mLumaData.get();
+ mImg.chroma_data = nullptr;
+ } else {
+ mLumaData = std::make_unique<uint8_t[]>(lumaSize);
+ mImg.data = mLumaData.get();
+ mChromaData = std::make_unique<uint8_t[]>(chromaSize);
+ mImg.chroma_data = mChromaData.get();
+ }
+ return true;
+}
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
+bool UhdrUnCompressedStructWrapper::loadRawResource(const char* fileName) {
+ if (!mImg.data) {
+ std::cerr << "memory is not allocated, read not possible" << std::endl;
+ return false;
+ }
+ std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int bpp = mFormat == YCbCr_p010 ? 2 : 1;
+ int size = ifd.tellg();
+ int length = mImg.width * mImg.height * bpp * 3 / 2; // 2x2 subsampling
+ if (size < length) {
+ std::cerr << "requested to read " << length << " bytes from file : " << fileName
+ << ", file contains only " << length << " bytes" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
+ char* mem = static_cast<char*>(mImg.data);
+ for (int i = 0; i < mImg.height; i++) {
+ ifd.read(mem, mImg.width * bpp);
+ mem += lumaStride * bpp;
+ }
+ if (!mIsChromaContiguous) {
+ mem = static_cast<char*>(mImg.chroma_data);
+ }
+ int chromaStride;
+ if (mIsChromaContiguous) {
+ chromaStride = mFormat == YCbCr_p010 ? lumaStride : lumaStride / 2;
+ } else {
+ if (mFormat == YCbCr_p010) {
+ chromaStride = mImg.chroma_stride == 0 ? lumaStride : mImg.chroma_stride;
+ } else {
+ chromaStride = mImg.chroma_stride == 0 ? (lumaStride / 2) : mImg.chroma_stride;
+ }
+ }
+ if (mFormat == YCbCr_p010) {
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, mImg.width * 2);
+ mem += chromaStride * 2;
+ }
+ } else {
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, (mImg.width / 2));
+ mem += chromaStride;
+ }
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, (mImg.width / 2));
+ mem += chromaStride;
+ }
+ }
+ return true;
+ }
+ std::cerr << "unable to open file : " << fileName << std::endl;
+ return false;
+}
- JpegR jpegRCodec;
+jr_uncompressed_ptr UhdrUnCompressedStructWrapper::getImageHandle() {
+ return &mImg;
+}
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+UhdrCompressedStructWrapper::UhdrCompressedStructWrapper(uint32_t width, uint32_t height) {
+ mWidth = width;
+ mHeight = height;
+}
- // test quality factor
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- -1, nullptr)) << "fail, API allows bad jpeg quality factor";
+bool UhdrCompressedStructWrapper::allocateMemory() {
+ if (mWidth == 0 || (mWidth % 2 != 0) || mHeight == 0 || (mHeight % 2 != 0)) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
+ }
+ int maxLength = std::max(8 * 1024 /* min size 8kb */, (int)(mWidth * mHeight * 3 * 2));
+ mData = std::make_unique<uint8_t[]>(maxLength);
+ mImg.data = mData.get();
+ mImg.length = 0;
+ mImg.maxLength = maxLength;
+ return true;
+}
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- 101, nullptr)) << "fail, API allows bad jpeg quality factor";
+jr_compressed_ptr UhdrCompressedStructWrapper::getImageHandle() {
+ return &mImg;
+}
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+static bool writeFile(const char* filename, void*& result, int length) {
+ std::ofstream ofd(filename, std::ios::binary);
+ if (ofd.is_open()) {
+ ofd.write(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to write to file : " << filename << std::endl;
+ return false;
+}
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+static bool readFile(const char* fileName, void*& result, int maxLength, int& length) {
+ std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ length = ifd.tellg();
+ if (length > maxLength) {
+ std::cerr << "not enough space to read file" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ ifd.read(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to read file : " << fileName << std::endl;
+ return false;
+}
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileName) {
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ ASSERT_EQ(OK, jpegHdr.getJPEGRInfo(img, &info));
+ ASSERT_EQ(kImageWidth, info.width);
+ ASSERT_EQ(kImageHeight, info.height);
+ size_t outSize = info.width * info.height * 8;
+ std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
+ jpegr_uncompressed_struct destImage{};
+ destImage.data = data.get();
+ ASSERT_EQ(OK, jpegHdr.decodeJPEGR(img, &destImage));
+ ASSERT_EQ(kImageWidth, destImage.width);
+ ASSERT_EQ(kImageHeight, destImage.height);
+#ifdef DUMP_OUTPUT
+ if (!writeFile(outFileName, destImage.data, outSize)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+}
+
+// ============================================================================
+// Unit Tests
+// ============================================================================
+
+// Test Encode API-0 invalid arguments
+TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
+ nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image";
+ {
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1)));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+ }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride";
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
- mRawP010ImageWithStride.chroma_data = nullptr;
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride";
- free(jpegR.data);
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
}
/* Test Encode API-1 invalid arguments */
-TEST_F(JpegRTest, encodeAPI1ForInvalidArgs) {
- int ret;
+TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
+ JpegR uHdrLib;
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
- JpegR jpegRCodec;
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawYuv420Image.data = malloc(16);
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- // test quality factor
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, -1, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, 101, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- nullptr, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
+ nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image";
+ {
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride";
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
// test 420 input
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = nullptr;
- mRawP010ImageWithStride.chroma_stride = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr for 420 image";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr 420 image";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image width";
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr 420 image";
+ }
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
- mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image height";
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad 420 color gamut";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride for 420";
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad 420 color gamut";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = mRawYuv420Image.data;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows chroma pointer for 420";
+ rawImg420->width = kWidth - 1;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width for 420";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = nullptr;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut";
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height for 420";
- mRawYuv420Image.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut";
+ rawImg420->width = 0;
+ rawImg420->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width for 420";
- free(jpegR.data);
+ rawImg420->width = kWidth;
+ rawImg420->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows luma stride for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = 0;
+ rawImg420->chroma_data = rawImgP010->data;
+ rawImg420->chroma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma pointer for 420";
+ }
}
/* Test Encode API-2 invalid arguments */
-TEST_F(JpegRTest, encodeAPI2ForInvalidArgs) {
- int ret;
+TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
+ JpegR uHdrLib;
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
- JpegR jpegRCodec;
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawYuv420Image.data = malloc(16);
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR)) << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test compressed image
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr p010 image";
+ {
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad luma stride";
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad chroma stride";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
// test 420 input
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = nullptr;
- mRawP010ImageWithStride.chroma_stride = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr for 420 image";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr 420 image";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 image width";
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr 420 image";
+ }
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
- mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 image height";
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad 420 color gamut";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad luma stride for 420";
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad 420 color gamut";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = mRawYuv420Image.data;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows chroma pointer for 420";
+ rawImg420->width = kWidth - 1;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width for 420";
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = nullptr;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height for 420";
- mRawYuv420Image.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
+ rawImg420->width = 0;
+ rawImg420->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width for 420";
- // bad compressed image
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
+ rawImg420->width = kWidth;
+ rawImg420->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height for 420";
- free(jpegR.data);
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows luma stride for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = 0;
+ rawImg420->chroma_data = rawImgP010->data;
+ rawImg420->chroma_stride = kWidth;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma pointer for 420";
+ }
}
/* Test Encode API-3 invalid arguments */
-TEST_F(JpegRTest, encodeAPI3ForInvalidArgs) {
- int ret;
+TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
+ JpegR uHdrLib;
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
- JpegR jpegRCodec;
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, static_cast<ultrahdr_transfer_function>(-10),
- &jpegR)) << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test compressed image
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr p010 image";
+ {
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image width";
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image height";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad luma stride";
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad chroma stride";
- mRawP010ImageWithStride.chroma_data = nullptr;
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
- // bad compressed image
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride";
- free(jpegR.data);
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
}
/* Test Encode API-4 invalid arguments */
-TEST_F(JpegRTest, encodeAPI4ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
+TEST(JpegRTest, EncodeAPI4WithInvalidArgs) {
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ JpegR uHdrLib;
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, &jpegR, nullptr, nullptr)) << "fail, API allows nullptr dest";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
// test primary image
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &jpegR, nullptr, &jpegR)) << "fail, API allows nullptr primary image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(), nullptr, jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr primary image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg2.getImageHandle(), jpgImg.getImageHandle(), nullptr,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr primary image";
// test gain map
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, nullptr, &jpegR)) << "fail, API allows nullptr gainmap image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), nullptr, nullptr, jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr gain map image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg2.getImageHandle(), nullptr,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr gain map image";
// test metadata
ultrahdr_metadata_struct good_metadata;
@@ -832,82 +1254,95 @@
ultrahdr_metadata_struct metadata = good_metadata;
metadata.version = "1.1";
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata version";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata version";
metadata = good_metadata;
metadata.minContentBoost = 3.0f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata content boost";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata content boost";
metadata = good_metadata;
metadata.gamma = -0.1f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata gamma";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata gamma";
metadata = good_metadata;
metadata.offsetSdr = -0.1f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset sdr";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata offset sdr";
metadata = good_metadata;
metadata.offsetHdr = -0.1f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset hdr";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata offset hdr";
metadata = good_metadata;
metadata.hdrCapacityMax = 0.5f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity max";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata hdr capacity max";
metadata = good_metadata;
metadata.hdrCapacityMin = 0.5f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity min";
-
- free(jpegR.data);
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata hdr capacity min";
}
/* Test Decode API invalid arguments */
-TEST_F(JpegRTest, decodeAPIForInvalidArgs) {
- int ret;
+TEST(JpegRTest, DecodeAPIWithInvalidArgs) {
+ JpegR uHdrLib;
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- // we are not really decoding anything so lets keep allocs to a minimum
- mRawP010Image.data = malloc(16);
-
- JpegR jpegRCodec;
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ jpegr_uncompressed_struct destImage{};
+ size_t outSize = 16 * 16 * 8;
+ std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
+ destImage.data = data.get();
// test jpegr image
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- nullptr, &mRawP010Image)) << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(nullptr, &destImage), OK)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_TRUE(jpgImg.allocateMemory());
// test dest image
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, nullptr)) << "fail, API allows nullptr for dest";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), nullptr), OK)
+ << "fail, API allows nullptr for dest";
+ destImage.data = nullptr;
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
+ << "fail, API allows nullptr for dest";
+ destImage.data = data.get();
// test max display boost
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5)) << "fail, API allows invalid max display boost";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, 0.5), OK)
+ << "fail, API allows invalid max display boost";
// test output format
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5, nullptr,
- static_cast<ultrahdr_output_format>(-1))) << "fail, API allows invalid output format";
-
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5, nullptr,
- static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)))
- << "fail, API allows invalid output format";
-
- free(jpegR.data);
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
+ static_cast<ultrahdr_output_format>(-1)),
+ OK)
+ << "fail, API allows invalid output format";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
+ static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)),
+ OK)
+ << "fail, API allows invalid output format";
}
-TEST_F(JpegRTest, writeXmpThenRead) {
+TEST(JpegRTest, writeXmpThenRead) {
ultrahdr_metadata_struct metadata_expected;
metadata_expected.version = "1.0";
metadata_expected.maxContentBoost = 1.25f;
@@ -918,16 +1353,16 @@
metadata_expected.hdrCapacityMin = 1.0f;
metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
std::string xmp = generateXmpForSecondaryImage(metadata_expected);
std::vector<uint8_t> xmpData;
xmpData.reserve(nameSpaceLength + xmp.size());
xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(nameSpace.c_str()),
- reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
+ reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
- reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
+ reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
ultrahdr_metadata_struct metadata_read;
EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
@@ -940,436 +1375,441 @@
EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax);
}
-/* Test Encode API-0 */
-TEST_F(JpegRTest, encodeFromP010) {
- int ret;
-
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010Image, true)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+/* Test Encode API-0 and Decode */
+TEST(JpegRTest, EncodeAPI0AndDecodeTest) {
+ // reference encode
+ UhdrUnCompressedStructWrapper rawImg(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_TRUE(rawImg.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2.setChromaMode(false));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2.setChromaMode(false));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
+ auto jpg1 = jpgImg.getImageHandle();
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api0_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
}
+#endif
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH + 128;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithStride, true)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
-
- jpegr_compressed_struct jpegRWithStride;
- jpegRWithStride.maxLength = jpegR.length;
- jpegRWithStride.data = malloc(jpegRWithStride.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegRWithStride,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- ASSERT_EQ(jpegR.length, jpegRWithStride.length)
- << "Same input is yielding different output";
- ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithStride.data, jpegR.length))
- << "Same input is yielding different output";
-
- mRawP010ImageWithChromaData.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithChromaData.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithChromaData.luma_stride = TEST_IMAGE_WIDTH + 64;
- mRawP010ImageWithChromaData.chroma_stride = TEST_IMAGE_WIDTH + 256;
- mRawP010ImageWithChromaData.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithChromaData, false)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- jpegr_compressed_struct jpegRWithChromaData;
- jpegRWithChromaData.maxLength = jpegR.length;
- jpegRWithChromaData.data = malloc(jpegRWithChromaData.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithChromaData, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegRWithChromaData, DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- ASSERT_EQ(jpegR.length, jpegRWithChromaData.length)
- << "Same input is yielding different output";
- ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithChromaData.data, jpegR.length))
- << "Same input is yielding different output";
-
- free(jpegR.data);
- free(jpegRWithStride.data);
- free(jpegRWithChromaData.data);
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api0_output.rgb"));
}
-/* Test Encode API-0 and decode */
-TEST_F(JpegRTest, encodeFromP010ThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+/* Test Encode API-1 and Decode */
+TEST(JpegRTest, EncodeAPI1AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
+ auto jpg1 = jpgImg.getImageHandle();
- free(jpegR.data);
- free(decodedJpegR.data);
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api1_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api1_output.rgb"));
}
-/* Test Encode API-0 (with stride) and decode */
-TEST_F(JpegRTest, encodeFromP010WithStrideThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE_WITH_STRIDE, mRawP010ImageWithStride.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE_WITH_STRIDE << " failed";
+/* Test Encode API-2 and Decode */
+TEST(JpegRTest, EncodeAPI2AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgSdr.allocateMemory());
+ auto sdr = jpgSdr.getImageHandle();
+ ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
+ auto jpg1 = jpgImg.getImageHandle();
- free(jpegR.data);
- free(decodedJpegR.data);
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api2_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api2_output.rgb"));
}
-/* Test Encode API-1 and decode */
-TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+/* Test Encode API-3 and Decode */
+TEST(JpegRTest, EncodeAPI3AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgSdr.allocateMemory());
+ auto sdr = jpgSdr.getImageHandle();
+ ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
+ auto jpg1 = jpgImg.getImageHandle();
- free(jpegR.data);
- free(decodedJpegR.data);
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api3_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api3_output.rgb"));
}
-/* Test Encode API-2 and decode */
-TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
- int ret;
+// ============================================================================
+// Profiling
+// ============================================================================
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+class Profiler {
+public:
+ void timerStart() { gettimeofday(&mStartingTime, nullptr); }
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ void timerStop() { gettimeofday(&mEndingTime, nullptr); }
- if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
- FAIL() << "Load file " << JPEG_IMAGE << " failed";
- }
- mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mRawYuv420Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_jpeg_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
+ int64_t elapsedTime() {
+ struct timeval elapsedMicroseconds;
+ elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
+ elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
+ return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
}
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
+private:
+ struct timeval mStartingTime;
+ struct timeval mEndingTime;
+};
- free(jpegR.data);
- free(decodedJpegR.data);
+class JpegRBenchmark : public JpegR {
+public:
+ void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
+ void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
+
+private:
+ const int kProfileCount = 10;
+};
+
+void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
+ jr_uncompressed_ptr p010Image,
+ ultrahdr_metadata_ptr metadata,
+ jr_uncompressed_ptr map) {
+ ASSERT_EQ(yuv420Image->width, p010Image->width);
+ ASSERT_EQ(yuv420Image->height, p010Image->height);
+ Profiler profileGenerateMap;
+ profileGenerateMap.timerStart();
+ for (auto i = 0; i < kProfileCount; i++) {
+ ASSERT_EQ(OK,
+ generateGainMap(yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ metadata, map));
+ if (i != kProfileCount - 1) delete[] static_cast<uint8_t*>(map->data);
+ }
+ profileGenerateMap.timerStop();
+ ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
+ profileGenerateMap.elapsedTime() / (kProfileCount * 1000.f));
}
-/* Test Encode API-3 and decode */
-TEST_F(JpegRTest, encodeFromJpegThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
+ ultrahdr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
+ Profiler profileRecMap;
+ profileRecMap.timerStart();
+ for (auto i = 0; i < kProfileCount; i++) {
+ ASSERT_EQ(OK,
+ applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
+ metadata->maxContentBoost /* displayBoost */, dest));
}
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (SAVE_INPUT_RGBA) {
- size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t);
- uint32_t *data = (uint32_t *)malloc(rgbaSize);
-
- for (size_t y = 0; y < mRawP010Image.height; ++y) {
- for (size_t x = 0; x < mRawP010Image.width; ++x) {
- Color hdr_yuv_gamma = getP010Pixel(&mRawP010Image, x, y);
- Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
- uint32_t rgba1010102 = colorToRgba1010102(hdr_rgb_gamma);
- size_t pixel_idx = x + y * mRawP010Image.width;
- reinterpret_cast<uint32_t*>(data)[pixel_idx] = rgba1010102;
- }
- }
-
- // Output image data to file
- std::string filePath = "/sdcard/Documents/input_from_p010.rgb10";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)data, rgbaSize);
- free(data);
- }
- if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
- FAIL() << "Load file " << JPEG_IMAGE << " failed";
- }
- mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_jpeg_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
- }
-
- free(jpegR.data);
- free(decodedJpegR.data);
+ profileRecMap.timerStop();
+ ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
+ profileRecMap.elapsedTime() / (kProfileCount * 1000.f));
}
-TEST_F(JpegRTest, ProfileGainMapFuncs) {
- const size_t kWidth = TEST_IMAGE_WIDTH;
- const size_t kHeight = TEST_IMAGE_HEIGHT;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = kWidth;
- mRawP010Image.height = kHeight;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawYuv420Image.width = kWidth;
- mRawYuv420Image.height = kHeight;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
+TEST(JpegRTest, ProfileGainMapFuncs) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ ultrahdr_metadata_struct metadata = {.version = "1.0"};
+ jpegr_uncompressed_struct map = {.data = NULL,
+ .width = 0,
+ .height = 0,
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
JpegRBenchmark benchmark;
+ ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(rawImg420.getImageHandle(),
+ rawImgP010.getImageHandle(), &metadata,
+ &map));
- ultrahdr_metadata_struct metadata = { .version = "1.0" };
-
- jpegr_uncompressed_struct map = { .data = NULL,
+ const int dstSize = kImageWidth * kImageWidth * 4;
+ auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
+ jpegr_uncompressed_struct dest = {.data = bufferDst.get(),
.width = 0,
.height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
- benchmark.BenchmarkGenerateGainMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map);
-
- const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4;
- auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
- jpegr_uncompressed_struct dest = { .data = bufferDst.get(),
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
-
- benchmark.BenchmarkApplyGainMap(&mRawYuv420Image, &map, &metadata, &dest);
+ ASSERT_NO_FATAL_FAILURE(
+ benchmark.BenchmarkApplyGainMap(rawImg420.getImageHandle(), &map, &metadata, &dest));
}
} // namespace android::ultrahdr
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 654e5b7..8d0eb59 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -169,11 +169,6 @@
}
}
- // Return true if native GLES drivers should be used and ANGLE is already loaded.
- if (android::GraphicsEnv::getInstance().shouldUseNativeDriver() && cnx->angleLoaded) {
- return true;
- }
-
// Return true if updated driver namespace is set.
ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (ns) {
@@ -245,28 +240,16 @@
if (!hnd) {
// Secondly, try to load from driver apk.
hnd = attempt_to_load_updated_driver(cnx);
-
- // If updated driver apk is set but fail to load, abort here.
- LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(),
- "couldn't find an OpenGL ES implementation from %s",
- android::GraphicsEnv::getInstance().getDriverPath().c_str());
}
- // Attempt to load native GLES drivers specified by ro.hardware.egl if native is selected.
- // If native is selected but fail to load, abort.
- if (!hnd && android::GraphicsEnv::getInstance().shouldUseNativeDriver()) {
- auto driverSuffix = base::GetProperty(RO_DRIVER_SUFFIX_PROPERTY, "");
- LOG_ALWAYS_FATAL_IF(driverSuffix.empty(),
- "Native GLES driver is selected but not specified in %s",
- RO_DRIVER_SUFFIX_PROPERTY);
- hnd = attempt_to_load_system_driver(cnx, driverSuffix.c_str(), true);
- LOG_ALWAYS_FATAL_IF(!hnd, "Native GLES driver is selected but failed to load. %s=%s",
- RO_DRIVER_SUFFIX_PROPERTY, driverSuffix.c_str());
- }
-
- // Finally, try to load default driver.
bool failToLoadFromDriverSuffixProperty = false;
if (!hnd) {
+ // If updated driver apk is set but fail to load, abort here.
+ if (android::GraphicsEnv::getInstance().getDriverNamespace()) {
+ LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
+ android::GraphicsEnv::getInstance().getDriverPath().c_str());
+ }
+ // Finally, try to load system driver.
// Start by searching for the library name appended by the system
// properties of the GLES userspace driver in both locations.
// i.e.:
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 88001b2..440eb17 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -49,6 +49,7 @@
#include "egl_trace.h"
using namespace android;
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
// ----------------------------------------------------------------------------
@@ -406,7 +407,7 @@
// ----------------------------------------------------------------------------
// Translates EGL color spaces to Android data spaces.
-static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace, PixelFormat pixelFormat) {
if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
return HAL_DATASPACE_UNKNOWN;
} else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
@@ -424,7 +425,13 @@
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_HLG_EXT) {
return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
- return HAL_DATASPACE_BT2020_LINEAR;
+ if (pixelFormat == PixelFormat::RGBA_FP16) {
+ return static_cast<android_dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_LINEAR |
+ HAL_DATASPACE_RANGE_EXTENDED);
+ } else {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ }
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
return HAL_DATASPACE_BT2020_PQ;
}
@@ -573,8 +580,6 @@
newList.push_back(EGL_NONE);
}
-using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
-
// Gets the native pixel format corrsponding to the passed EGLConfig.
void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
PixelFormat* format) {
@@ -714,7 +719,7 @@
return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
}
- android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+ android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace, format);
// Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN.
// HAL_DATASPACE_UNKNOWN is the default value, but it may have changed
// at this point.
diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp
index 8681784..c88f2fc 100644
--- a/services/displayservice/Android.bp
+++ b/services/displayservice/Android.bp
@@ -23,7 +23,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library_static {
name: "libdisplayservicehidl",
srcs: [
@@ -37,18 +37,24 @@
"libgui",
"libhidlbase",
"libutils",
+ ],
+
+ static_libs: [
"android.frameworks.displayservice@1.0",
],
export_include_dirs: ["include"],
export_shared_lib_headers: [
- "android.frameworks.displayservice@1.0",
"libgui",
"libutils",
],
+ export_static_lib_headers: [
+ "android.frameworks.displayservice@1.0",
+ ],
+
cflags: [
"-Werror",
"-Wall",
- ]
+ ],
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index dc7c75a..18f6dbc 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -28,6 +28,7 @@
cc_defaults {
name: "inputflinger_defaults",
+ host_supported: true,
cpp_std: "c++20",
cflags: [
"-Wall",
@@ -50,6 +51,20 @@
"-*", // Disable all checks not explicitly enabled for now
] + inputflinger_tidy_checks,
tidy_checks_as_errors: inputflinger_tidy_checks,
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ cflags: [
+ "-D__ANDROID_HOST__",
+ ],
+ },
+ },
}
/////////////////////////////////////////////////
@@ -79,6 +94,7 @@
"libcrypto",
"libcutils",
"libhidlbase",
+ "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
@@ -95,30 +111,22 @@
android: {
shared_libs: [
"libgui",
- "libinput",
"libstatspull",
"libstatssocket",
],
},
host: {
static_libs: [
- "libinput",
"libstatspull",
"libstatssocket",
],
- include_dirs: [
- "bionic/libc/kernel/android/uapi/",
- "bionic/libc/kernel/uapi",
- ],
- cflags: [
- "-D__ANDROID_HOST__",
- ],
},
},
}
cc_library_shared {
name: "libinputflinger",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
"libinputflinger_defaults",
@@ -179,25 +187,13 @@
"libbase",
"libbinder",
"libcutils",
+ "libinput",
"liblog",
"libutils",
],
header_libs: [
"libinputflinger_headers",
],
- target: {
- android: {
- shared_libs: [
- "libinput",
- ],
- },
- host: {
- static_libs: [
- "libinput",
- "libui-types",
- ],
- },
- },
}
cc_library_shared {
@@ -224,6 +220,7 @@
// native targets
"libgui_test",
"libinput",
+ "libinputreader_static",
"libinputflinger",
"inputflinger_tests",
"inputflinger_benchmarks",
@@ -246,12 +243,13 @@
"inputflinger_keyboard_input_fuzzer",
"inputflinger_multitouch_input_fuzzer",
"inputflinger_switch_input_fuzzer",
+ "inputflinger_touchpad_input_fuzzer",
"inputflinger_input_reader_fuzzer",
"inputflinger_blocking_queue_fuzzer",
"inputflinger_input_classifier_fuzzer",
// Java/Kotlin targets
- "CtsWindowManagerDeviceTestCases",
+ "CtsWindowManagerDeviceWindow",
"InputTests",
"CtsHardwareTestCases",
"CtsInputTestCases",
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 37b3187..c903564 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -186,7 +186,7 @@
dump += " InputFlinger dump\n";
- ::write(fd, dump.c_str(), dump.size());
+ TEMP_FAILURE_RETRY(::write(fd, dump.c_str(), dump.size()));
return NO_ERROR;
}
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 0fa47d1..c34cd53 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -91,8 +91,8 @@
readTime(readTime),
videoFrames(videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties.push_back(pointerProperties[i]);
- this->pointerCoords.push_back(pointerCoords[i]);
+ this->pointerProperties.emplace_back(pointerProperties[i]);
+ this->pointerCoords.emplace_back(pointerCoords[i]);
}
}
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index cdc4c08..c2da5ba 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -1,10 +1,10 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
@@ -116,6 +116,14 @@
]
},
{
+ "name": "CtsAppTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.cts.ToolbarActionBarTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
@@ -142,10 +150,10 @@
],
"hwasan-postsubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
@@ -246,6 +254,14 @@
]
},
{
+ "name": "CtsAppTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.cts.ToolbarActionBarTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 4e2a6fb..e200f8b 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -19,6 +19,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcrypto",
"libcutils",
"libinputflinger_base",
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b2e274d..6dd785a 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -82,8 +82,6 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
-
bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
return true; // dispatch event normally
}
@@ -270,7 +268,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
- dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
NotifyMotionArgs motionArgs = generateMotionArgs();
@@ -305,7 +303,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
- dispatcher.setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
for (auto _ : state) {
MotionEvent event = generateMotionEvent();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 492551e..8b57730 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -59,6 +59,7 @@
"libbase",
"libcrypto",
"libcutils",
+ "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
@@ -74,14 +75,12 @@
android: {
shared_libs: [
"libgui",
- "libinput",
"libstatspull",
"libstatssocket",
],
},
host: {
static_libs: [
- "libinput",
"libstatspull",
"libstatssocket",
],
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a670ebe..cb369a8 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -235,8 +235,8 @@
downTime(downTime),
pointerCount(pointerCount) {
for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
+ this->pointerProperties[i] = pointerProperties[i];
+ this->pointerCoords[i] = pointerCoords[i];
}
}
@@ -321,7 +321,28 @@
globalScaleFactor(globalScaleFactor),
deliveryTime(0),
resolvedAction(0),
- resolvedFlags(0) {}
+ resolvedFlags(0) {
+ switch (this->eventEntry->type) {
+ case EventEntry::Type::KEY: {
+ const KeyEntry& keyEntry = static_cast<KeyEntry&>(*this->eventEntry);
+ resolvedEventId = keyEntry.id;
+ resolvedAction = keyEntry.action;
+ resolvedFlags = keyEntry.flags;
+
+ break;
+ }
+ case EventEntry::Type::MOTION: {
+ const MotionEntry& motionEntry = static_cast<MotionEntry&>(*this->eventEntry);
+ resolvedEventId = motionEntry.id;
+ resolvedAction = motionEntry.action;
+ resolvedFlags = motionEntry.flags;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
uint32_t DispatchEntry::nextSeq() {
// Sequence number 0 is reserved and will never be returned.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0e268e9..2923a3c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -119,6 +119,10 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
+bool isEmpty(const std::stringstream& ss) {
+ return ss.rdbuf()->in_avail() == 0;
+}
+
inline const std::string binderToString(const sp<IBinder>& binder) {
if (binder == nullptr) {
return "<null>";
@@ -130,11 +134,6 @@
return uid.toString();
}
-inline int32_t getMotionEventActionPointerIndex(int32_t action) {
- return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
- AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-}
-
Result<void> checkKeyAction(int32_t action) {
switch (action) {
case AKEY_EVENT_ACTION_DOWN:
@@ -602,7 +601,7 @@
return {entry.xCursorPosition, entry.yCursorPosition};
}
- const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action);
+ const int32_t pointerIndex = MotionEvent::getActionIndex(entry.action);
return {entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X),
entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)};
}
@@ -2305,7 +2304,7 @@
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
- const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ const int32_t pointerIndex = MotionEvent::getActionIndex(action);
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2338,6 +2337,8 @@
} else if (isSplit) {
// New window does not support splitting but we have already split events.
// Ignore the new window.
+ LOG(INFO) << "Skipping " << newTouchedWindowHandle->getName()
+ << " because it doesn't support split touch";
newTouchedWindowHandle = nullptr;
}
} else {
@@ -2552,15 +2553,16 @@
// Update the pointerIds for non-splittable when it received pointer down.
if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
// If no split, we suppose all touched windows should receive pointer down.
- const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ const int32_t pointerIndex = MotionEvent::getActionIndex(action);
for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
TouchedWindow& touchedWindow = tempTouchState.windows[i];
// Ignore drag window for it should just track one pointer.
if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
continue;
}
- touchedWindow.addTouchingPointer(entry.deviceId,
- entry.pointerProperties[pointerIndex].id);
+ std::bitset<MAX_POINTER_ID + 1> touchingPointers;
+ touchingPointers.set(entry.pointerProperties[pointerIndex].id);
+ touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers);
}
}
}
@@ -2690,9 +2692,6 @@
"Conflicting pointer actions: Hover received while pointer was down.");
*outConflictingPointerActions = true;
}
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- }
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
@@ -2707,18 +2706,9 @@
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
- for (size_t i = 0; i < tempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- touchedWindow.removeTouchingPointer(entry.deviceId, pointerId);
- if (!touchedWindow.hasTouchingPointers(entry.deviceId)) {
- tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
- continue;
- }
- i += 1;
- }
+ const int32_t pointerIndex = MotionEvent::getActionIndex(action);
+ const uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
+ tempTouchState.removeTouchingPointer(entry.deviceId, pointerId);
}
// Save changes unless the action was scroll in which case the temporary touch
@@ -2817,7 +2807,7 @@
}
case AMOTION_EVENT_ACTION_POINTER_UP:
- if (getMotionEventActionPointerIndex(entry.action) != pointerIndex) {
+ if (MotionEvent::getActionIndex(entry.action) != pointerIndex) {
break;
}
// The drag pointer is up.
@@ -2980,7 +2970,7 @@
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
- dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
+ dumpWindowForTouchOcclusion(otherInfo, /*isTouchedWindow=*/false));
}
// canBeObscuredBy() has returned true above, which means this window is untrusted, so
// we perform the checks below to see if the touch can be propagated or not based on the
@@ -3008,8 +2998,7 @@
}
}
if (DEBUG_TOUCH_OCCLUSION) {
- info.debugInfo.push_back(
- dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
+ info.debugInfo.push_back(dumpWindowForTouchOcclusion(windowInfo, /*isTouchedWindow=*/true));
}
return info;
}
@@ -3303,10 +3292,6 @@
switch (newEntry.type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
- dispatchEntry->resolvedEventId = keyEntry.id;
- dispatchEntry->resolvedAction = keyEntry.action;
- dispatchEntry->resolvedFlags = keyEntry.flags;
-
if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
@@ -3334,7 +3319,6 @@
} else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
} else {
- dispatchEntry->resolvedAction = motionEntry.action;
dispatchEntry->resolvedEventId = motionEntry.id;
}
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
@@ -3350,7 +3334,6 @@
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
- dispatchEntry->resolvedFlags = motionEntry.flags;
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
}
@@ -3598,8 +3581,8 @@
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
+ LOG(INFO) << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
}
// Publish the key event.
@@ -3617,8 +3600,8 @@
case EventEntry::Type::MOTION: {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
+ LOG(INFO) << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
}
status = publishMotionEvent(*connection, *dispatchEntry);
break;
@@ -3777,8 +3760,8 @@
const std::shared_ptr<Connection>& connection,
bool notify) {
if (DEBUG_DISPATCH_CYCLE) {
- LOG(DEBUG) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
- << " - notify=" << toString(notify);
+ LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
+ << " - notify=" << toString(notify);
}
// Clear the dispatch queues.
@@ -4094,9 +4077,9 @@
uint32_t pointerId = uint32_t(pointerProperties.id);
if (pointerIds.test(pointerId)) {
splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
- splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
- splitPointerCoords[splitPointerCount].copyFrom(
- originalMotionEntry.pointerCoords[originalPointerIndex]);
+ splitPointerProperties[splitPointerCount] = pointerProperties;
+ splitPointerCoords[splitPointerCount] =
+ originalMotionEntry.pointerCoords[originalPointerIndex];
splitPointerCount += 1;
}
}
@@ -4118,7 +4101,7 @@
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+ int32_t originalPointerIndex = MotionEvent::getActionIndex(action);
const PointerProperties& pointerProperties =
originalMotionEntry.pointerProperties[originalPointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
@@ -4354,7 +4337,7 @@
validateMotionEvent(args.action, args.actionButton, args.getPointerCount(),
args.pointerProperties.data());
if (!motionCheck.ok()) {
- LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
+ LOG(FATAL) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
return;
}
@@ -4513,6 +4496,10 @@
std::unique_ptr<DeviceResetEntry> newEntry =
std::make_unique<DeviceResetEntry>(args.id, args.eventTime, args.deviceId);
needWake = enqueueInboundEventLocked(std::move(newEntry));
+
+ for (auto& [_, verifier] : mVerifiersByDisplay) {
+ verifier.resetDevice(args.deviceId);
+ }
} // release lock
if (needWake) {
@@ -4551,10 +4538,10 @@
}
if (debugInboundEventDetails()) {
- LOG(DEBUG) << __func__ << ": targetUid=" << toString(targetUid, &uidString)
- << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
- << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
- << ", event=" << *event;
+ LOG(INFO) << __func__ << ": targetUid=" << toString(targetUid, &uidString)
+ << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
+ << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
+ << ", event=" << *event;
}
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
@@ -4705,7 +4692,7 @@
bool needWake = false;
while (!injectedEntries.empty()) {
if (DEBUG_INJECTION) {
- LOG(DEBUG) << "Injecting " << injectedEntries.front()->getDescription();
+ LOG(INFO) << "Injecting " << injectedEntries.front()->getDescription();
}
needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
@@ -4769,8 +4756,8 @@
} // release lock
if (DEBUG_INJECTION) {
- LOG(DEBUG) << "injectInputEvent - Finished with result "
- << ftl::enum_string(injectionResult);
+ LOG(INFO) << "injectInputEvent - Finished with result "
+ << ftl::enum_string(injectionResult);
}
return injectionResult;
@@ -4814,8 +4801,8 @@
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
if (DEBUG_INJECTION) {
- LOG(DEBUG) << "Setting input event injection result to "
- << ftl::enum_string(injectionResult);
+ LOG(INFO) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
}
if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
@@ -5070,19 +5057,6 @@
mWindowHandlesByDisplay[displayId] = newHandles;
}
-void InputDispatcher::setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) {
- // TODO(b/198444055): Remove setInputWindows from InputDispatcher.
- { // acquire lock
- std::scoped_lock _l(mLock);
- for (const auto& [displayId, handles] : handlesPerDisplay) {
- setInputWindowsLocked(handles, displayId);
- }
- }
- // Wake up poll loop since it may need to make new input dispatching choices.
- mLooper->wake();
-}
-
/**
* Called from InputManagerService, update window handle list by displayId that can receive input.
* A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -5130,13 +5104,6 @@
// Copy old handles for release if they are no longer present.
const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
- // Save the old windows' orientation by ID before it gets updated.
- std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
- for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
- oldWindowOrientations.emplace(handle->getId(),
- handle->getInfo()->transform.getOrientation());
- }
-
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
@@ -5190,23 +5157,6 @@
}
}
- // Determine if the orientation of any of the input windows have changed, and cancel all
- // pointer events if necessary.
- for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
- const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
- if (newWindowHandle != nullptr &&
- newWindowHandle->getInfo()->transform.getOrientation() !=
- oldWindowOrientations[oldWindowHandle->getId()]) {
- std::shared_ptr<InputChannel> inputChannel =
- getInputChannelLocked(newWindowHandle->getToken());
- if (inputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "touched window's orientation changed");
- synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
- }
- }
- }
-
// Release information for windows that are no longer present.
// This ensures that unused input channels are released promptly.
// Otherwise, they might stick around until the window handle is destroyed
@@ -5459,8 +5409,8 @@
}
std::set<int32_t> deviceIds = touchedWindow->getTouchingDeviceIds();
if (deviceIds.size() != 1) {
- LOG(DEBUG) << "Can't transfer touch. Currently touching devices: " << dumpSet(deviceIds)
- << " for window: " << touchedWindow->dump();
+ LOG(INFO) << "Can't transfer touch. Currently touching devices: " << dumpSet(deviceIds)
+ << " for window: " << touchedWindow->dump();
return false;
}
const int32_t deviceId = *deviceIds.begin();
@@ -5807,6 +5757,12 @@
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
}
+ std::stringstream inputStateDump;
+ inputStateDump << connection->inputState;
+ if (!isEmpty(inputStateDump)) {
+ dump += INDENT3 "InputState: ";
+ dump += inputStateDump.str() + "\n";
+ }
}
} else {
dump += INDENT "Connections: <none>\n";
@@ -6725,7 +6681,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
// Set an empty list to remove all handles from the specific display.
- setInputWindowsLocked(/* window handles */ {}, displayId);
+ setInputWindowsLocked(/*windowInfoHandles=*/{}, displayId);
setFocusedApplicationLocked(displayId, nullptr);
// Call focus resolver to clean up stale requests. This must be called after input windows
// have been removed for the removed display.
@@ -6814,13 +6770,6 @@
mLooper->wake();
}
-void InputDispatcher::requestRefreshConfiguration() {
- InputDispatcherConfiguration config = mPolicy.getDispatcherConfiguration();
-
- std::scoped_lock _l(mLock);
- mConfig = config;
-}
-
void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) {
std::scoped_lock _l(mLock);
mMonitorDispatchingTimeout = timeout;
@@ -6933,4 +6882,11 @@
return nullptr;
}
+void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
+ std::scoped_lock _l(mLock);
+
+ mConfig.keyRepeatTimeout = timeout;
+ mConfig.keyRepeatDelay = delay;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 2b8b37e..fef726f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,7 +25,6 @@
#include "InputDispatcherConfiguration.h"
#include "InputDispatcherInterface.h"
#include "InputDispatcherPolicyInterface.h"
-#include "InputState.h"
#include "InputTarget.h"
#include "InputThread.h"
#include "LatencyAggregator.h"
@@ -110,9 +109,6 @@
std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
- void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>&
- handlesPerDisplay) override;
void setFocusedApplication(
int32_t displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
@@ -148,11 +144,11 @@
void cancelCurrentTouch() override;
- void requestRefreshConfiguration() override;
-
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
+ void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override;
+
private:
enum class DropReason {
NOT_DROPPED,
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 2fcb89a..ccffe26 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -28,7 +28,7 @@
InputState::~InputState() {}
-bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
+bool InputState::isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const {
for (const MotionMemento& memento : mMotionMementos) {
if (memento.deviceId == deviceId && memento.source == source &&
memento.displayId == displayId && memento.hovering) {
@@ -240,8 +240,8 @@
continue;
}
}
- pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]);
- pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]);
+ pointerProperties[pointerCount] = entry.pointerProperties[i];
+ pointerCoords[pointerCount] = entry.pointerCoords[i];
pointerCount++;
}
}
@@ -251,8 +251,8 @@
if (other.firstNewPointerIdx < 0) {
other.firstNewPointerIdx = other.pointerCount;
}
- other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
- other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
+ other.pointerProperties[other.pointerCount] = pointerProperties[i];
+ other.pointerCoords[other.pointerCount] = pointerCoords[i];
other.pointerCount++;
}
}
@@ -324,17 +324,16 @@
// We will deliver all pointers the target already knows about
for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
- pointerProperties[i].copyFrom(memento.pointerProperties[i]);
- pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+ pointerProperties[i] = memento.pointerProperties[i];
+ pointerCoords[i] = memento.pointerCoords[i];
pointerCount++;
}
// We will send explicit events for all pointers the target doesn't know about
for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
i < memento.pointerCount; i++) {
-
- pointerProperties[i].copyFrom(memento.pointerProperties[i]);
- pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+ pointerProperties[i] = memento.pointerProperties[i];
+ pointerCoords[i] = memento.pointerCoords[i];
pointerCount++;
// Down only if the first pointer, pointer down otherwise
@@ -370,8 +369,8 @@
std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
for (uint32_t pointerIdx = 0; pointerIdx < memento.pointerCount; pointerIdx++) {
uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
- pointerProperties[pointerIdx].copyFrom(memento.pointerProperties[pointerIdx]);
- pointerCoords[pointerIdx].copyFrom(memento.pointerCoords[pointerIdx]);
+ pointerProperties[pointerIdx] = memento.pointerProperties[pointerIdx];
+ pointerCoords[pointerIdx] = memento.pointerCoords[pointerIdx];
if (pointerIds.test(pointerId)) {
canceledPointerIndices.push_back(pointerIdx);
}
@@ -526,4 +525,14 @@
}
}
+std::ostream& operator<<(std::ostream& out, const InputState& state) {
+ if (!state.mMotionMementos.empty()) {
+ out << "mMotionMementos: ";
+ for (const InputState::MotionMemento& memento : state.mMotionMementos) {
+ out << "{deviceId= " << memento.deviceId << ", hovering=" << memento.hovering << "}, ";
+ }
+ }
+ return out;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index af2a3cb..32df034 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -36,7 +36,7 @@
// Returns true if the specified source is known to have received a hover enter
// motion event.
- bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
+ bool isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const;
// Records tracking information for a key event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
@@ -76,7 +76,7 @@
private:
struct KeyMemento {
- int32_t deviceId;
+ DeviceId deviceId;
uint32_t source;
int32_t displayId;
int32_t keyCode;
@@ -88,7 +88,7 @@
};
struct MotionMemento {
- int32_t deviceId;
+ DeviceId deviceId;
uint32_t source;
int32_t displayId;
int32_t flags;
@@ -128,7 +128,10 @@
std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers(
const MotionMemento& memento, std::bitset<MAX_POINTER_ID + 1> pointerIds,
nsecs_t currentTime);
+ friend std::ostream& operator<<(std::ostream& out, const InputState& state);
};
+std::ostream& operator<<(std::ostream& out, const InputState& state);
+
} // namespace inputdispatcher
} // namespace android
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index fc8b785..11f3413 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -76,4 +76,24 @@
}
return out;
}
+
+std::ostream& operator<<(std::ostream& out, const InputTarget& target) {
+ out << "{inputChannel=";
+ if (target.inputChannel != nullptr) {
+ out << target.inputChannel->getName();
+ } else {
+ out << "<null>";
+ }
+ out << ", windowHandle=";
+ if (target.windowHandle != nullptr) {
+ out << target.windowHandle->getName();
+ } else {
+ out << "<null>";
+ }
+ out << ", targetFlags=" << target.flags.string();
+ out << ", pointers=" << target.getPointerInfoString();
+ out << "}";
+ return out;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 3bf8b68..8b8a35a 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -139,6 +139,6 @@
std::string getPointerInfoString() const;
};
-std::string dispatchModeToString(int32_t dispatchMode);
+std::ostream& operator<<(std::ostream& out, const InputTarget& target);
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index dadfdc1..4221e42 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -40,25 +40,25 @@
return out;
}
-bool TouchState::hasTouchingPointers(int32_t deviceId) const {
+bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
return window.hasTouchingPointers(deviceId);
});
}
-void TouchState::removeTouchingPointer(int32_t removedDeviceId, int32_t pointerId) {
+void TouchState::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.removeTouchingPointer(removedDeviceId, pointerId);
+ touchedWindow.removeTouchingPointer(deviceId, pointerId);
}
clearWindowsWithoutPointers();
}
void TouchState::removeTouchingPointerFromWindow(
- int32_t removedDeviceId, int32_t pointerId,
+ DeviceId deviceId, int32_t pointerId,
const sp<android::gui::WindowInfoHandle>& windowHandle) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.removeTouchingPointer(removedDeviceId, pointerId);
+ touchedWindow.removeTouchingPointer(deviceId, pointerId);
clearWindowsWithoutPointers();
return;
}
@@ -79,8 +79,7 @@
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags,
- int32_t addedDeviceId,
+ ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget) {
for (TouchedWindow& touchedWindow : windows) {
@@ -94,9 +93,9 @@
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
- touchedWindow.addTouchingPointers(addedDeviceId, touchingPointerIds);
+ touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
if (firstDownTimeInTarget) {
- touchedWindow.trySetDownTimeInTarget(addedDeviceId, *firstDownTimeInTarget);
+ touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
return;
}
@@ -104,25 +103,25 @@
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
- touchedWindow.addTouchingPointers(addedDeviceId, touchingPointerIds);
+ touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
if (firstDownTimeInTarget) {
- touchedWindow.trySetDownTimeInTarget(addedDeviceId, *firstDownTimeInTarget);
+ touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
windows.push_back(touchedWindow);
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
- int32_t hoveringDeviceId, int32_t hoveringPointerId) {
+ DeviceId deviceId, int32_t hoveringPointerId) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
- touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
windows.push_back(touchedWindow);
}
@@ -149,12 +148,12 @@
}
}
-void TouchState::cancelPointersForWindowsExcept(int32_t touchedDeviceId,
+void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token) {
std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
if (w.windowHandle->getToken() != token) {
- w.removeTouchingPointers(touchedDeviceId, pointerIds);
+ w.removeTouchingPointers(deviceId, pointerIds);
}
});
clearWindowsWithoutPointers();
@@ -168,10 +167,10 @@
*/
void TouchState::cancelPointersForNonPilferingWindows() {
// First, find all pointers that are being pilfered, across all windows
- std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
+ std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
for (const TouchedWindow& w : windows) {
- for (const auto& [iterDeviceId, pilferedPointerIds] : w.getPilferingPointers()) {
- allPilferedPointerIdsByDevice[iterDeviceId] |= pilferedPointerIds;
+ for (const auto& [deviceId, pilferedPointerIds] : w.getPilferingPointers()) {
+ allPilferedPointerIdsByDevice[deviceId] |= pilferedPointerIds;
}
};
@@ -183,12 +182,12 @@
// (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
// pilfered pointers will be disjoint across all windows, but there's no reason to cause that
// limitation here.
- for (const auto& [iterDeviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
+ for (const auto& [deviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
- w.getPilferingPointers(iterDeviceId) ^ allPilferedPointerIds;
+ w.getPilferingPointers(deviceId) ^ allPilferedPointerIds;
// Remove all pointers pilfered by other windows
- w.removeTouchingPointers(iterDeviceId, pilferedByOtherWindows);
+ w.removeTouchingPointers(deviceId, pilferedByOtherWindows);
});
}
clearWindowsWithoutPointers();
@@ -248,11 +247,11 @@
[](const TouchedWindow& window) { return window.hasHoveringPointers(); });
}
-std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
+std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
int32_t pointerId) const {
std::set<sp<WindowInfoHandle>> out;
for (const TouchedWindow& window : windows) {
- if (window.hasHoveringPointer(hoveringDeviceId, pointerId)) {
+ if (window.hasHoveringPointer(deviceId, pointerId)) {
out.insert(window.windowHandle);
}
}
@@ -266,10 +265,10 @@
clearWindowsWithoutPointers();
}
-void TouchState::removeAllPointersForDevice(int32_t removedDeviceId) {
+void TouchState::removeAllPointersForDevice(DeviceId deviceId) {
for (TouchedWindow& window : windows) {
- window.removeAllHoveringPointersForDevice(removedDeviceId);
- window.removeAllTouchingPointersForDevice(removedDeviceId);
+ window.removeAllHoveringPointersForDevice(deviceId);
+ window.removeAllTouchingPointersForDevice(deviceId);
}
clearWindowsWithoutPointers();
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9f29a4a..39e63e5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -38,27 +38,27 @@
void reset();
void clearWindowsWithoutPointers();
- std::set<int32_t> getActiveDeviceIds() const;
+ std::set<DeviceId> getActiveDeviceIds() const;
- bool hasTouchingPointers(int32_t device) const;
- void removeTouchingPointer(int32_t deviceId, int32_t pointerId);
- void removeTouchingPointerFromWindow(int32_t deviceId, int32_t pointerId,
+ bool hasTouchingPointers(DeviceId deviceId) const;
+ void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
+ void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
const sp<android::gui::WindowInfoHandle>& windowHandle);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags, int32_t deviceId,
+ ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t deviceId, int32_t hoveringPointerId);
- void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
+ DeviceId deviceId, int32_t hoveringPointerId);
+ void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
void clearHoveringPointers();
- void removeAllPointersForDevice(int32_t deviceId);
+ void removeAllPointersForDevice(DeviceId deviceId);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
// Cancel pointers for current set of windows except the window with particular binder token.
- void cancelPointersForWindowsExcept(int32_t deviceId,
+ void cancelPointersForWindowsExcept(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token);
// Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
@@ -75,7 +75,7 @@
bool hasHoveringPointers() const;
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
- int32_t deviceId, int32_t pointerId) const;
+ DeviceId deviceId, int32_t pointerId) const;
std::string dump() const;
};
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index ae16520..9807a6d 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -35,7 +35,7 @@
return false;
}
-bool TouchedWindow::hasHoveringPointers(int32_t deviceId) const {
+bool TouchedWindow::hasHoveringPointers(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return false;
@@ -53,7 +53,7 @@
std::erase_if(mDeviceStates, [](const auto& pair) { return !pair.second.hasPointers(); });
}
-bool TouchedWindow::hasHoveringPointer(int32_t deviceId, int32_t pointerId) const {
+bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return false;
@@ -63,15 +63,11 @@
return state.hoveringPointerIds.test(pointerId);
}
-void TouchedWindow::addHoveringPointer(int32_t deviceId, int32_t pointerId) {
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, int32_t pointerId) {
mDeviceStates[deviceId].hoveringPointerIds.set(pointerId);
}
-void TouchedWindow::addTouchingPointer(int32_t deviceId, int32_t pointerId) {
- mDeviceStates[deviceId].touchingPointerIds.set(pointerId);
-}
-
-void TouchedWindow::addTouchingPointers(int32_t deviceId,
+void TouchedWindow::addTouchingPointers(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointers) {
mDeviceStates[deviceId].touchingPointerIds |= pointers;
}
@@ -85,15 +81,15 @@
return false;
}
-bool TouchedWindow::hasTouchingPointers(int32_t deviceId) const {
+bool TouchedWindow::hasTouchingPointers(DeviceId deviceId) const {
return getTouchingPointers(deviceId).any();
}
-bool TouchedWindow::hasTouchingPointer(int32_t deviceId, int32_t pointerId) const {
+bool TouchedWindow::hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const {
return getTouchingPointers(deviceId).test(pointerId);
}
-std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(int32_t deviceId) const {
+std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return {};
@@ -103,14 +99,14 @@
return state.touchingPointerIds;
}
-void TouchedWindow::removeTouchingPointer(int32_t deviceId, int32_t pointerId) {
+void TouchedWindow::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId, true);
removeTouchingPointers(deviceId, pointerIds);
}
-void TouchedWindow::removeTouchingPointers(int32_t deviceId,
+void TouchedWindow::removeTouchingPointers(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointers) {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
@@ -126,23 +122,23 @@
}
}
-std::set<int32_t> TouchedWindow::getTouchingDeviceIds() const {
- std::set<int32_t> deviceIds;
+std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const {
+ std::set<DeviceId> deviceIds;
for (const auto& [deviceId, _] : mDeviceStates) {
deviceIds.insert(deviceId);
}
return deviceIds;
}
-std::set<int32_t> TouchedWindow::getActiveDeviceIds() const {
- std::set<int32_t> out;
+std::set<DeviceId> TouchedWindow::getActiveDeviceIds() const {
+ std::set<DeviceId> out;
for (const auto& [deviceId, _] : mDeviceStates) {
out.emplace(deviceId);
}
return out;
}
-bool TouchedWindow::hasPilferingPointers(int32_t deviceId) const {
+bool TouchedWindow::hasPilferingPointers(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return false;
@@ -152,16 +148,16 @@
return state.pilferingPointerIds.any();
}
-void TouchedWindow::addPilferingPointers(int32_t deviceId,
+void TouchedWindow::addPilferingPointers(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds) {
mDeviceStates[deviceId].pilferingPointerIds |= pointerIds;
}
-void TouchedWindow::addPilferingPointer(int32_t deviceId, int32_t pointerId) {
+void TouchedWindow::addPilferingPointer(DeviceId deviceId, int32_t pointerId) {
mDeviceStates[deviceId].pilferingPointerIds.set(pointerId);
}
-std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getPilferingPointers(int32_t deviceId) const {
+std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getPilferingPointers(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return {};
@@ -171,15 +167,15 @@
return state.pilferingPointerIds;
}
-std::map<int32_t, std::bitset<MAX_POINTER_ID + 1>> TouchedWindow::getPilferingPointers() const {
- std::map<int32_t, std::bitset<MAX_POINTER_ID + 1>> out;
+std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> TouchedWindow::getPilferingPointers() const {
+ std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> out;
for (const auto& [deviceId, state] : mDeviceStates) {
out.emplace(deviceId, state.pilferingPointerIds);
}
return out;
}
-std::optional<nsecs_t> TouchedWindow::getDownTimeInTarget(int32_t deviceId) const {
+std::optional<nsecs_t> TouchedWindow::getDownTimeInTarget(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return {};
@@ -188,7 +184,7 @@
return state.downTimeInTarget;
}
-void TouchedWindow::trySetDownTimeInTarget(int32_t deviceId, nsecs_t downTime) {
+void TouchedWindow::trySetDownTimeInTarget(DeviceId deviceId, nsecs_t downTime) {
auto [stateIt, _] = mDeviceStates.try_emplace(deviceId);
DeviceState& state = stateIt->second;
@@ -197,7 +193,7 @@
}
}
-void TouchedWindow::removeAllTouchingPointersForDevice(int32_t deviceId) {
+void TouchedWindow::removeAllTouchingPointersForDevice(DeviceId deviceId) {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return;
@@ -213,7 +209,7 @@
}
}
-void TouchedWindow::removeHoveringPointer(int32_t deviceId, int32_t pointerId) {
+void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return;
@@ -227,7 +223,7 @@
}
}
-void TouchedWindow::removeAllHoveringPointersForDevice(int32_t deviceId) {
+void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 81393fc..0a38f9f 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -34,43 +34,42 @@
// Hovering
bool hasHoveringPointers() const;
- bool hasHoveringPointers(int32_t deviceId) const;
- bool hasHoveringPointer(int32_t deviceId, int32_t pointerId) const;
- void addHoveringPointer(int32_t deviceId, int32_t pointerId);
- void removeHoveringPointer(int32_t deviceId, int32_t pointerId);
+ bool hasHoveringPointers(DeviceId deviceId) const;
+ bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
+ void addHoveringPointer(DeviceId deviceId, int32_t pointerId);
+ void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
// Touching
- bool hasTouchingPointer(int32_t deviceId, int32_t pointerId) const;
+ bool hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const;
bool hasTouchingPointers() const;
- bool hasTouchingPointers(int32_t deviceId) const;
- std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(int32_t deviceId) const;
- void addTouchingPointer(int32_t deviceId, int32_t pointerId);
- void addTouchingPointers(int32_t deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
- void removeTouchingPointer(int32_t deviceId, int32_t pointerId);
- void removeTouchingPointers(int32_t deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ bool hasTouchingPointers(DeviceId deviceId) const;
+ std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(DeviceId deviceId) const;
+ void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
+ void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
/**
* Get the currently active touching device id. If there isn't exactly 1 touching device, return
* nullopt.
*/
- std::set<int32_t> getTouchingDeviceIds() const;
+ std::set<DeviceId> getTouchingDeviceIds() const;
/**
* The ids of devices that are currently touching or hovering.
*/
- std::set<int32_t> getActiveDeviceIds() const;
+ std::set<DeviceId> getActiveDeviceIds() const;
// Pilfering pointers
- bool hasPilferingPointers(int32_t deviceId) const;
- void addPilferingPointers(int32_t deviceId, std::bitset<MAX_POINTER_ID + 1> pointerIds);
- void addPilferingPointer(int32_t deviceId, int32_t pointerId);
- std::bitset<MAX_POINTER_ID + 1> getPilferingPointers(int32_t deviceId) const;
- std::map<int32_t, std::bitset<MAX_POINTER_ID + 1>> getPilferingPointers() const;
+ bool hasPilferingPointers(DeviceId deviceId) const;
+ void addPilferingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointerIds);
+ void addPilferingPointer(DeviceId deviceId, int32_t pointerId);
+ std::bitset<MAX_POINTER_ID + 1> getPilferingPointers(DeviceId deviceId) const;
+ std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> getPilferingPointers() const;
// Down time
- std::optional<nsecs_t> getDownTimeInTarget(int32_t deviceId) const;
- void trySetDownTimeInTarget(int32_t deviceId, nsecs_t downTime);
+ std::optional<nsecs_t> getDownTimeInTarget(DeviceId deviceId) const;
+ void trySetDownTimeInTarget(DeviceId deviceId, nsecs_t downTime);
- void removeAllTouchingPointersForDevice(int32_t deviceId);
- void removeAllHoveringPointersForDevice(int32_t deviceId);
+ void removeAllTouchingPointersForDevice(DeviceId deviceId);
+ void removeAllHoveringPointersForDevice(DeviceId deviceId);
void clearHoveringPointers();
std::string dump() const;
@@ -88,7 +87,7 @@
bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); };
};
- std::map<int32_t /*deviceId*/, DeviceState> mDeviceStates;
+ std::map<DeviceId, DeviceState> mDeviceStates;
static std::string deviceStateToString(const TouchedWindow::DeviceState& state);
};
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 6a07e59..d099b44 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -87,14 +87,6 @@
*/
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
- /* Sets the list of input windows per display.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void setInputWindows(
- const std::unordered_map<int32_t, std::vector<sp<gui::WindowInfoHandle>>>&
- handlesPerDisplay) = 0;
-
/* Sets the focused application on the given display.
*
* This method may be called on any thread (usually by the input manager).
@@ -226,11 +218,10 @@
*/
virtual void cancelCurrentTouch() = 0;
- /**
- * Request that the InputDispatcher's configuration, which can be obtained through the policy,
- * be updated.
+ /*
+ * Updates key repeat configuration timeout and delay.
*/
- virtual void requestRefreshConfiguration() = 0;
+ virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 729d01f..af28e48 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -73,9 +73,6 @@
InputDeviceSensorAccuracy accuracy) = 0;
virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
- /* Gets the input dispatcher configuration. */
- virtual InputDispatcherConfiguration getDispatcherConfiguration() = 0;
-
/* Filters an input event.
* Return true to dispatch the event unmodified, false to consume the event.
* A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 88e1d7d..25e1d21 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -437,7 +437,8 @@
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) = 0;
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) = 0;
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index ccb8773..c0c6d1f 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -52,6 +52,7 @@
"mapper/RotaryEncoderInputMapper.cpp",
"mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
+ "mapper/SlopController.cpp",
"mapper/SwitchInputMapper.cpp",
"mapper/TouchCursorInputMapperCommon.cpp",
"mapper/TouchInputMapper.cpp",
@@ -79,6 +80,7 @@
"libcrypto",
"libcutils",
"libjsoncpp",
+ "libinput",
"liblog",
"libPlatformProperties",
"libstatslog",
@@ -97,13 +99,11 @@
target: {
android: {
shared_libs: [
- "libinput",
"libstatspull",
],
},
host: {
static_libs: [
- "libinput",
"libbinder",
"libstatspull",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 4d0e13e..e69c99e 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -538,7 +538,8 @@
associatedDevice(std::move(assocDev)),
controllerNumber(0),
enabled(true),
- isVirtual(fd < 0) {}
+ isVirtual(fd < 0),
+ currentFrameDropped(false) {}
EventHub::Device::~Device() {
close();
@@ -612,6 +613,45 @@
}
bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
+
+ // Query the initial state of keys and switches, which is tracked by EventHub.
+ readDeviceState();
+}
+
+void EventHub::Device::readDeviceState() {
+ if (readDeviceBitMask(EVIOCGKEY(0), keyState) < 0) {
+ ALOGD("Unable to query the global key state for %s: %s", path.c_str(), strerror(errno));
+ }
+ if (readDeviceBitMask(EVIOCGSW(0), swState) < 0) {
+ ALOGD("Unable to query the global switch state for %s: %s", path.c_str(), strerror(errno));
+ }
+
+ // Read absolute axis info and values for all available axes for the device.
+ populateAbsoluteAxisStates();
+}
+
+void EventHub::Device::populateAbsoluteAxisStates() {
+ absState.clear();
+
+ for (int axis = 0; axis <= ABS_MAX; axis++) {
+ if (!absBitmask.test(axis)) {
+ continue;
+ }
+ struct input_absinfo info {};
+ if (ioctl(fd, EVIOCGABS(axis), &info)) {
+ ALOGE("Error reading absolute controller %d for device %s fd %d: %s", axis,
+ identifier.name.c_str(), fd, strerror(errno));
+ continue;
+ }
+ auto& [axisInfo, value] = absState[axis];
+ axisInfo.valid = true;
+ axisInfo.minValue = info.minimum;
+ axisInfo.maxValue = info.maximum;
+ axisInfo.flat = info.flat;
+ axisInfo.fuzz = info.fuzz;
+ axisInfo.resolution = info.resolution;
+ value = info.value;
+ }
}
bool EventHub::Device::hasKeycodeLocked(int keycode) const {
@@ -729,6 +769,66 @@
return NAME_NOT_FOUND;
}
+void EventHub::Device::trackInputEvent(const struct input_event& event) {
+ switch (event.type) {
+ case EV_KEY: {
+ LOG_ALWAYS_FATAL_IF(!currentFrameDropped &&
+ !keyState.set(static_cast<size_t>(event.code),
+ event.value != 0),
+ "%s: device '%s' received invalid EV_KEY event code: %s value: %d",
+ __func__, identifier.name.c_str(),
+ InputEventLookup::getLinuxEvdevLabel(EV_KEY, event.code, 1)
+ .code.c_str(),
+ event.value);
+ break;
+ }
+ case EV_SW: {
+ LOG_ALWAYS_FATAL_IF(!currentFrameDropped &&
+ !swState.set(static_cast<size_t>(event.code),
+ event.value != 0),
+ "%s: device '%s' received invalid EV_SW event code: %s value: %d",
+ __func__, identifier.name.c_str(),
+ InputEventLookup::getLinuxEvdevLabel(EV_SW, event.code, 1)
+ .code.c_str(),
+ event.value);
+ break;
+ }
+ case EV_ABS: {
+ if (currentFrameDropped) {
+ break;
+ }
+ auto it = absState.find(event.code);
+ LOG_ALWAYS_FATAL_IF(it == absState.end(),
+ "%s: device '%s' received invalid EV_ABS event code: %s value: %d",
+ __func__, identifier.name.c_str(),
+ InputEventLookup::getLinuxEvdevLabel(EV_ABS, event.code, 0)
+ .code.c_str(),
+ event.value);
+ it->second.value = event.value;
+ break;
+ }
+ case EV_SYN: {
+ switch (event.code) {
+ case SYN_REPORT:
+ currentFrameDropped = false;
+ break;
+ case SYN_DROPPED:
+ // When we receive SYN_DROPPED, all events in the current frame should be
+ // dropped. We query the state of the device to synchronize our device state
+ // with the kernel's to account for the dropped events.
+ currentFrameDropped = true;
+ readDeviceState();
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
/**
* Get the capabilities for the current process.
* Crashes the system if unable to create / check / destroy the capabilities object.
@@ -864,30 +964,6 @@
strerror(errno));
}
-void EventHub::populateDeviceAbsoluteAxisInfo(Device& device) {
- for (int axis = 0; axis <= ABS_MAX; axis++) {
- if (!device.absBitmask.test(axis)) {
- continue;
- }
- struct input_absinfo info {};
- if (ioctl(device.fd, EVIOCGABS(axis), &info)) {
- ALOGE("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
- device.identifier.name.c_str(), device.fd, errno);
- continue;
- }
- if (info.minimum == info.maximum) {
- continue;
- }
- RawAbsoluteAxisInfo& outAxisInfo = device.rawAbsoluteAxisInfoCache[axis];
- outAxisInfo.valid = true;
- outAxisInfo.minValue = info.minimum;
- outAxisInfo.maxValue = info.maximum;
- outAxisInfo.flat = info.flat;
- outAxisInfo.fuzz = info.fuzz;
- outAxisInfo.resolution = info.resolution;
- }
-}
-
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
@@ -919,18 +995,21 @@
RawAbsoluteAxisInfo* outAxisInfo) const {
outAxisInfo->clear();
if (axis < 0 || axis > ABS_MAX) {
- return -1;
+ return NAME_NOT_FOUND;
}
std::scoped_lock _l(mLock);
- Device* device = getDeviceLocked(deviceId);
+ const Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
- return -1;
+ return NAME_NOT_FOUND;
}
- auto it = device->rawAbsoluteAxisInfoCache.find(axis);
- if (it == device->rawAbsoluteAxisInfoCache.end()) {
- return -1;
+ // We can read the RawAbsoluteAxisInfo even if the device is disabled and doesn't have a valid
+ // fd, because the info is populated once when the device is first opened, and it doesn't change
+ // throughout the device lifecycle.
+ auto it = device->absState.find(axis);
+ if (it == device->absState.end()) {
+ return NAME_NOT_FOUND;
}
- *outAxisInfo = it->second;
+ *outAxisInfo = it->second.info;
return OK;
}
@@ -962,38 +1041,34 @@
}
int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
- if (scanCode >= 0 && scanCode <= KEY_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
- if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
- return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
- }
- }
+ if (scanCode < 0 || scanCode > KEY_MAX) {
+ return AKEY_STATE_UNKNOWN;
}
- return AKEY_STATE_UNKNOWN;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->keyBitmask.test(scanCode)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+ return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
- std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode);
- if (scanCodes.size() != 0) {
- if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
- for (size_t i = 0; i < scanCodes.size(); i++) {
- int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) {
- return AKEY_STATE_DOWN;
- }
- }
- return AKEY_STATE_UP;
- }
- }
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->keyMap.haveKeyLayout()) {
+ return AKEY_STATE_UNKNOWN;
}
- return AKEY_STATE_UNKNOWN;
+ const std::vector<int32_t> scanCodes =
+ device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode);
+ if (scanCodes.empty()) {
+ return AKEY_STATE_UNKNOWN;
+ }
+ return std::any_of(scanCodes.begin(), scanCodes.end(),
+ [&device](const int32_t sc) {
+ return sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc);
+ })
+ ? AKEY_STATE_DOWN
+ : AKEY_STATE_UP;
}
int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
@@ -1037,39 +1112,33 @@
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
- if (sw >= 0 && sw <= SW_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
- if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) {
- return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
- }
- }
+ if (sw < 0 || sw > SW_MAX) {
+ return AKEY_STATE_UNKNOWN;
}
- return AKEY_STATE_UNKNOWN;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->swBitmask.test(sw)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+ return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
*outValue = 0;
-
- if (axis >= 0 && axis <= ABS_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
- struct input_absinfo info;
- if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
- device->identifier.name.c_str(), device->fd, errno);
- return -errno;
- }
-
- *outValue = info.value;
- return OK;
- }
+ if (axis < 0 || axis > ABS_MAX) {
+ return NAME_NOT_FOUND;
}
- return -1;
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd()) {
+ return NAME_NOT_FOUND;
+ }
+ const auto it = device->absState.find(axis);
+ if (it == device->absState.end()) {
+ return NAME_NOT_FOUND;
+ }
+ *outValue = it->second.value;
+ return OK;
}
bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
@@ -1922,6 +1991,7 @@
const size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
+ device->trackInputEvent(iev);
events.push_back({
.when = processEventTimestamp(iev),
.readTime = systemTime(SYSTEM_TIME_MONOTONIC),
@@ -2448,9 +2518,6 @@
device->configureFd();
- // read absolute axis info for all available axes for the device
- populateDeviceAbsoluteAxisInfo(*device);
-
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
@@ -2802,6 +2869,31 @@
device->associatedDevice
? device->associatedDevice->sysfsRootPath.c_str()
: "<none>");
+ if (device->keyBitmask.any(0, KEY_MAX + 1)) {
+ const auto pressedKeys = device->keyState.dumpSetIndices(", ", [](int i) {
+ return InputEventLookup::getLinuxEvdevLabel(EV_KEY, i, 1).code;
+ });
+ dump += StringPrintf(INDENT3 "KeyState (pressed): %s\n", pressedKeys.c_str());
+ }
+ if (device->swBitmask.any(0, SW_MAX + 1)) {
+ const auto pressedSwitches = device->swState.dumpSetIndices(", ", [](int i) {
+ return InputEventLookup::getLinuxEvdevLabel(EV_SW, i, 1).code;
+ });
+ dump += StringPrintf(INDENT3 "SwState (pressed): %s\n", pressedSwitches.c_str());
+ }
+ if (!device->absState.empty()) {
+ std::string axisValues;
+ for (const auto& [axis, state] : device->absState) {
+ if (!axisValues.empty()) {
+ axisValues += ", ";
+ }
+ axisValues += StringPrintf("%s=%d",
+ InputEventLookup::getLinuxEvdevLabel(EV_ABS, axis, 0)
+ .code.c_str(),
+ state.value);
+ }
+ dump += INDENT3 "AbsState: " + axisValues + "\n";
+ }
}
dump += INDENT "Unattached video devices:\n";
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0a64a1c..bacc720 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -48,6 +48,7 @@
mIdentifier(identifier),
mClasses(0),
mSources(0),
+ mIsWaking(false),
mIsExternal(false),
mHasMic(false),
mDropUntilNextSync(false) {}
@@ -101,6 +102,7 @@
dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str());
dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+ dump += StringPrintf(INDENT2 "IsWaking: %s\n", toString(mIsWaking));
dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
if (mAssociatedDisplayPort) {
dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
@@ -220,23 +222,7 @@
mAssociatedDeviceType =
getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
- }
-
- if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
- if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
- std::shared_ptr<KeyCharacterMap> keyboardLayout =
- mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
- bool shouldBumpGeneration = false;
- for_each_subdevice(
- [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
- if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
- shouldBumpGeneration = true;
- }
- });
- if (shouldBumpGeneration) {
- bumpGeneration();
- }
- }
+ mIsWaking = mConfiguration.getBool("device.wake").value_or(false);
}
if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
@@ -376,9 +362,25 @@
}
--count;
}
+ postProcess(out);
return out;
}
+void InputDevice::postProcess(std::list<NotifyArgs>& args) const {
+ if (mIsWaking) {
+ // Update policy flags to request wake for the `NotifyArgs` that come from waking devices.
+ for (auto& arg : args) {
+ if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) {
+ notifyMotionArgs->policyFlags |= POLICY_FLAG_WAKE;
+ } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) {
+ notifySwitchArgs->policyFlags |= POLICY_FLAG_WAKE;
+ } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) {
+ notifyKeyArgs->policyFlags |= POLICY_FLAG_WAKE;
+ }
+ }
+ }
+}
+
std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) {
std::list<NotifyArgs> out;
for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); });
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 024187f..0bcab42 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -19,6 +19,8 @@
#include <bitset>
#include <climits>
#include <filesystem>
+#include <functional>
+#include <map>
#include <ostream>
#include <string>
#include <unordered_map>
@@ -411,7 +413,17 @@
* Note the parameter "bit" is an index to the bit, 0 <= bit < BITS.
*/
inline bool test(size_t bit) const {
- return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false;
+ return (bit < BITS) && mData[bit / WIDTH].test(bit % WIDTH);
+ }
+ /* Sets the given bit in the bit array to given value.
+ * Returns true if the given bit is a valid index and thus was set successfully.
+ */
+ inline bool set(size_t bit, bool value) {
+ if (bit >= BITS) {
+ return false;
+ }
+ mData[bit / WIDTH].set(bit % WIDTH, value);
+ return true;
}
/* Returns total number of bytes needed for the array */
inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; }
@@ -459,6 +471,20 @@
mData[i] = std::bitset<WIDTH>(buffer[i]);
}
}
+ /* Dump the indices in the bit array that are set. */
+ inline std::string dumpSetIndices(std::string separator,
+ std::function<std::string(size_t /*index*/)> format) {
+ std::string dmp;
+ for (size_t i = 0; i < BITS; i++) {
+ if (test(i)) {
+ if (!dmp.empty()) {
+ dmp += separator;
+ }
+ dmp += format(i);
+ }
+ }
+ return dmp.empty() ? "<none>" : dmp;
+ }
private:
std::array<std::bitset<WIDTH>, COUNT> mData;
@@ -600,22 +626,26 @@
ftl::Flags<InputDeviceClass> classes;
- BitArray<KEY_MAX> keyBitmask;
- BitArray<KEY_MAX> keyState;
- BitArray<ABS_MAX> absBitmask;
- BitArray<REL_MAX> relBitmask;
- BitArray<SW_MAX> swBitmask;
- BitArray<SW_MAX> swState;
- BitArray<LED_MAX> ledBitmask;
- BitArray<FF_MAX> ffBitmask;
- BitArray<INPUT_PROP_MAX> propBitmask;
- BitArray<MSC_MAX> mscBitmask;
+ BitArray<KEY_CNT> keyBitmask;
+ BitArray<KEY_CNT> keyState;
+ BitArray<REL_CNT> relBitmask;
+ BitArray<SW_CNT> swBitmask;
+ BitArray<SW_CNT> swState;
+ BitArray<LED_CNT> ledBitmask;
+ BitArray<FF_CNT> ffBitmask;
+ BitArray<INPUT_PROP_CNT> propBitmask;
+ BitArray<MSC_CNT> mscBitmask;
+ BitArray<ABS_CNT> absBitmask;
+ struct AxisState {
+ RawAbsoluteAxisInfo info;
+ int value;
+ };
+ std::map<int /*axis*/, AxisState> absState;
std::string configurationFile;
std::unique_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
- std::unordered_map<int /*axis*/, RawAbsoluteAxisInfo> rawAbsoluteAxisInfoCache;
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
@@ -644,6 +674,7 @@
status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
void configureFd();
+ void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
@@ -653,6 +684,10 @@
void setLedForControllerLocked();
status_t mapLed(int32_t led, int32_t* outScanCode) const;
void setLedStateLocked(int32_t led, bool on);
+
+ bool currentFrameDropped;
+ void trackInputEvent(const struct input_event& event);
+ void readDeviceState();
};
/**
@@ -718,13 +753,6 @@
void addDeviceInputInotify();
void addDeviceInotify();
- /**
- * AbsoluteAxisInfo remains unchanged for the lifetime of the device, hence
- * we can read and store it with device
- * @param device target device
- */
- static void populateDeviceAbsoluteAxisInfo(Device& device);
-
// Protect all internal state.
mutable std::mutex mLock;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 2f8e5bd..1cbcbf4 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -191,6 +191,7 @@
std::unique_ptr<PeripheralControllerInterface> mController;
uint32_t mSources;
+ bool mIsWaking;
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
std::optional<std::string> mAssociatedDisplayUniqueId;
@@ -207,6 +208,10 @@
PropertyMap mConfiguration;
+ // Runs logic post a `process` call. This can be used to update the generated `NotifyArgs` as
+ // per the properties of the InputDevice.
+ void postProcess(std::list<NotifyArgs>& args) const;
+
// helpers to interate over the devices collection
// run a function against every mapper on every subdevice
inline void for_each_mapper(std::function<void(InputMapper&)> f) {
@@ -284,7 +289,18 @@
return mEventHub->getDeviceControllerNumber(mId);
}
inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
- return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo);
+ if (const auto status = mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); status != OK) {
+ return status;
+ }
+
+ // Validate axis info for InputDevice.
+ if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
+ // Historically, we deem axes with the same min and max values as invalid to avoid
+ // dividing by zero when scaling by max - min.
+ // TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used.
+ axisInfo->valid = false;
+ }
+ return OK;
}
inline bool hasRelativeAxis(int32_t code) const {
return mEventHub->hasRelativeAxis(mId, code);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index c684ed4..79f07a5 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -347,7 +347,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
}
@@ -357,7 +357,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
if (buttonsPressed) {
BitSet32 pressed(buttonsPressed);
@@ -371,7 +371,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
}
@@ -386,7 +386,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
// Send scroll events.
@@ -401,7 +401,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
}
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 099a955..8a9ea75 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -352,7 +352,7 @@
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
&pointerProperties, &pointerCoords, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
return out;
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 5c42e10..58b29b8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -86,20 +86,26 @@
return ADISPLAY_ID_NONE;
}
+std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() const {
+ if (mKeyboardLayoutInfo) {
+ return mKeyboardLayoutInfo;
+ }
+ std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
+ if (!layoutInfo) {
+ return std::nullopt;
+ }
+ return KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType);
+}
+
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
info.setKeyboardType(mKeyboardType);
info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
- if (mKeyboardLayoutInfo) {
- info.setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
- } else {
- std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
- if (layoutInfo) {
- info.setKeyboardLayoutInfo(
- KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType));
- }
+ std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
+ if (keyboardLayoutInfo) {
+ info.setKeyboardLayoutInfo(*keyboardLayoutInfo);
}
}
@@ -152,13 +158,31 @@
getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
mKeyboardLayoutInfo = newKeyboardLayoutInfo;
+ // Also update keyboard layout overlay as soon as we find the new layout info
+ updateKeyboardLayoutOverlay();
bumpGeneration();
}
}
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUTS)) {
+ if (!getDeviceContext().getDeviceClasses().test(InputDeviceClass::VIRTUAL) &&
+ updateKeyboardLayoutOverlay()) {
+ bumpGeneration();
+ }
+ }
return out;
}
+bool KeyboardInputMapper::updateKeyboardLayoutOverlay() {
+ std::shared_ptr<KeyCharacterMap> keyboardLayout =
+ getDeviceContext()
+ .getContext()
+ ->getPolicy()
+ ->getKeyboardLayoutOverlay(getDeviceContext().getDeviceIdentifier(),
+ getKeyboardLayoutInfo());
+ return getDeviceContext().setKeyboardLayoutOverlay(keyboardLayout);
+}
+
void KeyboardInputMapper::configureParameters() {
const PropertyMap& config = getDeviceContext().getConfiguration();
mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
@@ -205,6 +229,7 @@
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
+ int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
&policyFlags)) {
@@ -226,6 +251,7 @@
// key repeat, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns[*keyDownIndex].keyCode;
downTime = mKeyDowns[*keyDownIndex].downTime;
+ flags = mKeyDowns[*keyDownIndex].flags;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
@@ -234,12 +260,14 @@
}
if (policyFlags & POLICY_FLAG_GESTURE) {
out += getDeviceContext().cancelTouch(when, readTime);
+ flags |= AKEY_EVENT_FLAG_KEEP_TOUCH_MODE;
}
KeyDown keyDown;
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
keyDown.downTime = when;
+ keyDown.flags = flags;
mKeyDowns.push_back(keyDown);
}
onKeyDownProcessed();
@@ -249,6 +277,7 @@
// key up, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns[*keyDownIndex].keyCode;
downTime = mKeyDowns[*keyDownIndex].downTime;
+ flags = mKeyDowns[*keyDownIndex].flags;
mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
} else {
// key was not actually down
@@ -282,9 +311,8 @@
out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
mSource, getDisplayId(), policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
- downTime));
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags,
+ keyCode, scanCode, keyMetaState, downTime));
return out;
}
@@ -411,7 +439,7 @@
out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
getDisplayId(), /*policyFlags=*/0, AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+ mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
mKeyDowns[i].downTime));
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 96044eb..09808df 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -57,6 +57,7 @@
nsecs_t downTime{};
int32_t keyCode{};
int32_t scanCode{};
+ int32_t flags{};
};
uint32_t mSource{};
@@ -98,6 +99,8 @@
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
std::optional<size_t> findKeyDownIndex(int32_t scanCode);
+ std::optional<KeyboardLayoutInfo> getKeyboardLayoutInfo() const;
+ bool updateKeyboardLayoutOverlay();
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 13f2e59..07ae5b1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,7 @@
#include "RotaryEncoderInputMapper.h"
+#include <utils/Timers.h>
#include <optional>
#include "CursorScrollAccumulator.h"
@@ -62,6 +63,7 @@
dump += INDENT2 "Rotary Encoder Input Mapper:\n";
dump += StringPrintf(INDENT3 "HaveWheel: %s\n",
toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
+ dump += StringPrintf(INDENT3 "HaveSlopController: %s\n", toString(mSlopController != nullptr));
}
std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when,
@@ -70,6 +72,16 @@
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
if (!changes.any()) {
mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
+
+ const PropertyMap& propertyMap = getDeviceContext().getConfiguration();
+ float slopThreshold = propertyMap.getInt("rotary_encoder.slop_threshold").value_or(0);
+ int32_t slopDurationNs = milliseconds_to_nanoseconds(
+ propertyMap.getInt("rotary_encoder.slop_duration_ms").value_or(0));
+ if (slopThreshold > 0 && slopDurationNs > 0) {
+ mSlopController = std::make_unique<SlopController>(slopThreshold, slopDurationNs);
+ } else {
+ mSlopController = nullptr;
+ }
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
@@ -103,6 +115,10 @@
std::list<NotifyArgs> out;
float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+ if (mSlopController) {
+ scroll = mSlopController->consumeEvent(when, scroll);
+ }
+
bool scrolled = scroll != 0;
// Send motion event.
@@ -132,10 +148,10 @@
out.push_back(
NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, /* buttonState */ 0, MotionClassification::NONE,
+ metaState, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /*videoFrames=*/{}));
}
mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 9e2e8c4..fe5d152 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -20,6 +20,7 @@
#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
+#include "SlopController.h"
namespace android {
@@ -46,6 +47,7 @@
int32_t mSource;
float mScalingFactor;
ui::Rotation mOrientation;
+ std::unique_ptr<SlopController> mSlopController;
explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
new file mode 100644
index 0000000..f79219f
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+#include "../Macros.h"
+// clang-format on
+
+#include "SlopController.h"
+
+namespace {
+int signOf(float value) {
+ if (value == 0) return 0;
+ if (value > 0) return 1;
+ return -1;
+}
+} // namespace
+
+namespace android {
+
+SlopController::SlopController(float slopThreshold, nsecs_t slopDurationNanos)
+ : mSlopThreshold(slopThreshold), mSlopDurationNanos(slopDurationNanos) {}
+
+float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) {
+ if (mSlopDurationNanos == 0) {
+ return value;
+ }
+
+ if (shouldResetSlopTracking(eventTimeNanos, value)) {
+ mCumulativeValue = 0;
+ mHasSlopBeenMet = false;
+ }
+
+ mLastEventTimeNanos = eventTimeNanos;
+
+ if (mHasSlopBeenMet) {
+ // Since slop has already been met, we know that all of the current value would pass the
+ // slop threshold. So return that, without any further processing.
+ return value;
+ }
+
+ mCumulativeValue += value;
+
+ if (abs(mCumulativeValue) >= mSlopThreshold) {
+ mHasSlopBeenMet = true;
+ // Return the amount of value that exceeds the slop.
+ return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
+ }
+
+ return 0;
+}
+
+bool SlopController::shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const {
+ const nsecs_t ageNanos = eventTimeNanos - mLastEventTimeNanos;
+ if (ageNanos >= mSlopDurationNanos) {
+ return true;
+ }
+ if (value == 0) {
+ return false;
+ }
+ if (signOf(mCumulativeValue) != signOf(value)) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SlopController.h b/services/inputflinger/reader/mapper/SlopController.h
new file mode 100644
index 0000000..c106410
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SlopController.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Controls a slop logic. Slop here refers to an approach to try and drop insignificant input
+ * events. This is helpful in cases where unintentional input events may cause unintended outcomes,
+ * like scrolling a screen or keeping the screen awake.
+ *
+ * Current slop logic:
+ * "If time since last event > Xns, then discard the next N values."
+ */
+class SlopController final {
+public:
+ SlopController(float slopThreshold, nsecs_t slopDurationNanos);
+
+ /**
+ * Consumes an event with a given time and value for slop processing.
+ * Returns an amount <=value that should be consumed.
+ */
+ float consumeEvent(nsecs_t eventTime, float value);
+
+private:
+ bool shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) const;
+
+ /** The amount of event values ignored after an inactivity of the slop duration. */
+ const float mSlopThreshold;
+ /** The duration of inactivity that resets slop controlling. */
+ const nsecs_t mSlopDurationNanos;
+
+ nsecs_t mLastEventTimeNanos = 0;
+ float mCumulativeValue = 0;
+ bool mHasSlopBeenMet = false;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f2b0a4b..b565454 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -271,7 +271,7 @@
toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
mExternalStylusFusionTimeout);
- dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x",
+ dump += StringPrintf(INDENT4 "External Stylus Buttons Applied: 0x%08x\n",
mExternalStylusButtonsApplied);
dump += INDENT3 "External Stylus State:\n";
dumpStylusState(dump, mExternalStylusState);
@@ -972,7 +972,18 @@
(rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0;
const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
- const bool viewportChanged = mViewport != newViewport;
+ bool viewportChanged;
+ if (mParameters.enableForInactiveViewport) {
+ // When touch is enabled for an inactive viewport, ignore the
+ // viewport active status when checking whether the viewport has
+ // changed.
+ DisplayViewport tempViewport = mViewport;
+ tempViewport.isActive = newViewport.isActive;
+ viewportChanged = tempViewport != newViewport;
+ } else {
+ viewportChanged = mViewport != newViewport;
+ }
+
bool skipViewportUpdate = false;
if (viewportChanged) {
const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
@@ -1998,12 +2009,12 @@
PointerCoords& curOutCoords = outCoords[outIndex];
if (curInProperties != curOutProperties) {
- curOutProperties.copyFrom(curInProperties);
+ curOutProperties = curInProperties;
changed = true;
}
if (curInCoords != curOutCoords) {
- curOutCoords.copyFrom(curInCoords);
+ curOutCoords = curInCoords;
changed = true;
}
}
@@ -2733,7 +2744,7 @@
buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
// Update state.
@@ -2745,10 +2756,9 @@
for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- mPointerGesture.lastGestureProperties[index].copyFrom(
- mPointerGesture.currentGestureProperties[index]);
- mPointerGesture.lastGestureCoords[index].copyFrom(
- mPointerGesture.currentGestureCoords[index]);
+ mPointerGesture.lastGestureProperties[index] =
+ mPointerGesture.currentGestureProperties[index];
+ mPointerGesture.lastGestureCoords[index] = mPointerGesture.currentGestureCoords[index];
mPointerGesture.lastGestureIdToIndex[id] = index;
}
}
@@ -3532,8 +3542,7 @@
std::tie(x, y) = mPointerController->getPosition();
}
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedState.cookedPointerData.pointerCoords[index]);
+ mPointerSimple.currentCoords = mCurrentCookedState.cookedPointerData.pointerCoords[index];
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerSimple.currentProperties.id = 0;
@@ -3571,8 +3580,8 @@
const auto [x, y] = mPointerController->getPosition();
const uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
- mPointerSimple.currentCoords.copyFrom(
- mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
+ mPointerSimple.currentCoords =
+ mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex];
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
@@ -3632,7 +3641,7 @@
mOrientedXPrecision, mOrientedYPrecision,
mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
if (mPointerSimple.hovering && !hovering) {
@@ -3647,7 +3656,7 @@
&mPointerSimple.lastCoords, mOrientedXPrecision,
mOrientedYPrecision, mPointerSimple.lastCursorX,
mPointerSimple.lastCursorY, mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
if (down) {
@@ -3664,7 +3673,7 @@
&mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
- mPointerSimple.downTime, /* videoFrames */ {}));
+ mPointerSimple.downTime, /*videoFrames=*/{}));
}
// Send move.
@@ -3675,7 +3684,7 @@
&mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
- mPointerSimple.downTime, /* videoFrames */ {}));
+ mPointerSimple.downTime, /*videoFrames=*/{}));
}
if (hovering) {
@@ -3691,7 +3700,7 @@
&mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, cursorPosition.x, cursorPosition.y,
- mPointerSimple.downTime, /* videoFrames */ {}));
+ mPointerSimple.downTime, /*videoFrames=*/{}));
}
// Send hover move.
@@ -3702,7 +3711,7 @@
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
&mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
- cursorPosition.y, mPointerSimple.downTime, /* videoFrames */ {}));
+ cursorPosition.y, mPointerSimple.downTime, /*videoFrames=*/{}));
}
if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3712,8 +3721,7 @@
mWheelXVelocityControl.move(when, &hscroll, nullptr);
// Send scroll.
- PointerCoords pointerCoords;
- pointerCoords.copyFrom(mPointerSimple.currentCoords);
+ PointerCoords pointerCoords = mPointerSimple.currentCoords;
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
@@ -3724,13 +3732,13 @@
&mPointerSimple.currentProperties, &pointerCoords,
mOrientedXPrecision, mOrientedYPrecision, cursorPosition.x,
cursorPosition.y, mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
}
// Save state.
if (down || hovering) {
- mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
- mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+ mPointerSimple.lastCoords = mPointerSimple.currentCoords;
+ mPointerSimple.lastProperties = mPointerSimple.currentProperties;
mPointerSimple.displayId = displayId;
mPointerSimple.source = mSource;
mPointerSimple.lastCursorX = cursorPosition.x;
@@ -3755,7 +3763,7 @@
mOrientedXPrecision, mOrientedYPrecision,
mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
mPointerSimple.downTime,
- /* videoFrames */ {}));
+ /*videoFrames=*/{}));
if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
}
@@ -3784,8 +3792,8 @@
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = idToIndex[id];
- pointerProperties[pointerCount].copyFrom(properties[index]);
- pointerCoords[pointerCount].copyFrom(coords[index]);
+ pointerProperties[pointerCount] = properties[index];
+ pointerCoords[pointerCount] = coords[index];
if (changedId >= 0 && id == uint32_t(changedId)) {
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index d8b59ca..c5dfb00 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -195,9 +195,9 @@
enum class DeviceMode {
DISABLED, // input is disabled
DIRECT, // direct mapping (touchscreen)
- UNSCALED, // unscaled mapping (touchpad)
+ UNSCALED, // unscaled mapping (e.g. captured touchpad)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- POINTER, // pointer mapping (pointer)
+ POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
ftl_last = POINTER
};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 986dabb..eca0f86 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -350,6 +350,7 @@
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
dump += INDENT3 "Captured event converter:\n";
dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
}
std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
@@ -361,13 +362,31 @@
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
- std::optional<int32_t> displayId = mPointerController->getDisplayId();
+ mDisplayId = ADISPLAY_ID_NONE;
+ if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ // This InputDevice is associated with a viewport.
+ // Only generate events for the associated display.
+ const bool mismatchedPointerDisplay =
+ (viewport->displayId != mPointerController->getDisplayId());
+ if (mismatchedPointerDisplay) {
+ ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
+ "controller",
+ mDeviceContext.getName().c_str());
+ }
+ mDisplayId = mismatchedPointerDisplay ? std::nullopt
+ : std::make_optional(viewport->displayId);
+ } else {
+ // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+ mDisplayId = mPointerController->getDisplayId();
+ }
+
ui::Rotation orientation = ui::ROTATION_0;
- if (displayId.has_value()) {
- if (auto viewport = config.getDisplayViewportById(*displayId); viewport) {
+ if (mDisplayId.has_value()) {
+ if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
orientation = getInverseRotation(viewport->orientation);
}
}
+ mGestureConverter.setDisplayId(mDisplayId);
mGestureConverter.setOrientation(orientation);
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
@@ -497,13 +516,19 @@
std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out = {};
- MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
- for (Gesture& gesture : mGesturesToProcess) {
- out += mGestureConverter.handleGesture(when, readTime, gesture);
- metricsAccumulator.processGesture(mMetricsId, gesture);
+ if (mDisplayId) {
+ MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
+ for (Gesture& gesture : mGesturesToProcess) {
+ out += mGestureConverter.handleGesture(when, readTime, gesture);
+ metricsAccumulator.processGesture(mMetricsId, gesture);
+ }
}
mGesturesToProcess.clear();
return out;
}
+std::optional<int32_t> TouchpadInputMapper::getAssociatedDisplayId() {
+ return mDisplayId;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 73ca5af..47d712e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -64,6 +64,8 @@
using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/,
uint16_t /*productId*/, uint16_t /*version*/>;
+ std::optional<int32_t> getAssociatedDisplayId() override;
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
@@ -102,6 +104,11 @@
std::set<int32_t> mLastFrameTrackingIds;
// Tracking IDs for touches that have at some point been reported as palms by the touchpad.
std::set<int32_t> mPalmTrackingIds;
+
+ // The display that events generated by this mapper should target. This can be set to
+ // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
+ // std::nullopt), all events will be ignored.
+ std::optional<int32_t> mDisplayId;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 3abf2bd..7006e9e 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -124,6 +124,11 @@
std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
+ if (!mDisplayId) {
+ // Ignore gestures when there is no target display configured.
+ return {};
+ }
+
switch (gesture.type) {
case kGestureTypeMove:
return {handleMove(when, readTime, gesture)};
@@ -556,7 +561,7 @@
readTime,
mDeviceId,
SOURCE,
- mPointerController->getDisplayId(),
+ *mDisplayId,
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 3ea3790..e6cf617 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -46,6 +46,8 @@
void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+ void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; }
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -84,6 +86,7 @@
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ std::optional<int32_t> mDisplayId;
ui::Rotation mOrientation = ui::ROTATION_0;
RawAbsoluteAxisInfo mXAxisInfo;
RawAbsoluteAxisInfo mYAxisInfo;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 1585fdd..3d6df30 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -59,8 +59,10 @@
"NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
"PropertyProvider_test.cpp",
+ "SlopController_test.cpp",
"SyncQueue_test.cpp",
"TestInputListener.cpp",
+ "TestInputListenerMatchers.cpp",
"TouchpadInputMapper_test.cpp",
"KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
@@ -75,25 +77,9 @@
target: {
android: {
shared_libs: [
- "libinput",
"libvintf",
],
},
- host: {
- sanitize: {
- address: true,
- },
- include_dirs: [
- "bionic/libc/kernel/android/uapi/",
- "bionic/libc/kernel/uapi",
- ],
- cflags: [
- "-D__ANDROID_HOST__",
- ],
- static_libs: [
- "libinput",
- ],
- },
},
sanitize: {
hwaddress: true,
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 30222bf..78420c0 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -235,7 +235,7 @@
}
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) {
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 78bb2c3..e03d28d 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -86,7 +86,7 @@
int32_t /*deviceId*/) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) override;
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 4df0f69..74ce359 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -19,6 +19,7 @@
#include <EventHub.h>
#include <gestures/GestureConverter.h>
#include <gtest/gtest.h>
+#include <gui/constants.h>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
@@ -85,6 +86,7 @@
TEST_F(GestureConverterTest, Move) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -93,8 +95,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
}
@@ -103,6 +105,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -111,8 +114,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5));
}
@@ -120,6 +123,7 @@
TEST_F(GestureConverterTest, ButtonsChange) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
// Press left and right buttons at once
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -132,23 +136,23 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Then release the left button
Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -161,8 +165,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Finally release the right button
Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -174,22 +178,24 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, DragWithButton) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
// Press the button
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -201,15 +207,15 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Move
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
@@ -219,8 +225,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
@@ -234,23 +240,25 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll) {
const nsecs_t downTime = 12345;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
@@ -261,7 +269,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER), WithDownTime(downTime),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
@@ -269,7 +278,8 @@
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -280,7 +290,8 @@
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -292,7 +303,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_Rotated) {
@@ -300,6 +312,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
@@ -309,14 +322,15 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDownTime(downTime)));
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X - 10, POINTER_Y),
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -326,7 +340,7 @@
WithCoords(POINTER_X - 15, POINTER_Y),
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -337,12 +351,13 @@
WithCoords(POINTER_X - 15, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -358,12 +373,14 @@
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
ASSERT_EQ(1u, args.size());
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionClassification(MotionClassification::NONE));
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -387,6 +404,7 @@
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/0);
@@ -406,6 +424,7 @@
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
/*dy=*/5);
@@ -431,6 +450,7 @@
// only checks movement in one dimension.
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -444,7 +464,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -453,7 +474,8 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -462,7 +484,8 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
@@ -471,7 +494,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -488,7 +512,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -504,26 +529,30 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -535,28 +564,31 @@
NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10);
@@ -571,7 +603,8 @@
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
@@ -585,21 +618,24 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 10, /* dy= */ 0);
@@ -613,7 +649,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -622,7 +659,8 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -631,7 +669,8 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -640,7 +679,8 @@
3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger3Start = arg.pointerCoords[3];
args.pop_front();
@@ -649,7 +689,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
@@ -668,7 +709,8 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
@@ -686,32 +728,37 @@
3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -722,7 +769,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -730,7 +777,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -742,7 +789,7 @@
WithGesturePinchScaleFactor(0.8f, EPSILON),
WithPointerCoords(0, POINTER_X - 80, POINTER_Y),
WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -753,18 +800,19 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -775,7 +823,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -783,7 +831,7 @@
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 1.2, GESTURES_ZOOM_UPDATE);
@@ -795,7 +843,7 @@
WithGesturePinchScaleFactor(1.2f, EPSILON),
WithPointerCoords(0, POINTER_X - 120, POINTER_Y),
WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -806,18 +854,19 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -841,6 +890,7 @@
TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -866,6 +916,7 @@
TEST_F(GestureConverterTest, ResetWithButtonPressed) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
@@ -879,24 +930,25 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
(void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -909,12 +961,14 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/10);
@@ -927,24 +981,28 @@
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringPinch) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -957,18 +1015,19 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FlingTapDown) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
@@ -977,7 +1036,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X, POINTER_Y));
ASSERT_TRUE(mFakePointerController->isPointerShown());
@@ -987,6 +1047,7 @@
// Tap should produce button press/release events
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -996,7 +1057,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT,
@@ -1007,7 +1069,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
@@ -1015,29 +1078,32 @@
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f)));
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f)));
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Click) {
// Click should produce button press/release events
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1047,7 +1113,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT,
@@ -1058,7 +1125,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
@@ -1066,7 +1134,7 @@
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f)));
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
@@ -1078,17 +1146,19 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f)));
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
@@ -1097,6 +1167,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1106,7 +1177,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -1127,6 +1199,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1136,7 +1209,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT,
@@ -1147,7 +1221,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
@@ -1155,7 +1230,7 @@
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f)));
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
@@ -1167,17 +1242,19 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f)));
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Future taps should be re-enabled
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
@@ -1189,6 +1266,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -1197,7 +1275,8 @@
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 51202b7..6d9cd87 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,7 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
+#include "TestInputListenerMatchers.h"
#include <NotifyArgsBuilders.h>
#include <android-base/properties.h>
@@ -107,6 +108,23 @@
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+/**
+ * If we expect to receive the event, the timeout can be made very long. When the test are running
+ * correctly, we will actually never wait until the end of the timeout because the wait will end
+ * when the event comes in. Still, this value shouldn't be infinite. During development, a local
+ * change may cause the test to fail. This timeout should be short enough to not annoy so that the
+ * developer can see the failure quickly (on human scale).
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms;
+/**
+ * When no event is expected, we can have a very short timeout. A large value here would slow down
+ * the tests. In the unlikely event of system being too slow, the event may still be present but the
+ * timeout would complete before it is consumed. This would result in test flakiness. If this
+ * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this
+ * would get noticed and addressed quickly.
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms;
+
static constexpr int expectedWallpaperFlags =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
@@ -136,41 +154,10 @@
<< MotionEvent::actionToString(receivedAction);
}
-MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
- bool matches = action == arg.getAction();
- if (!matches) {
- *result_listener << "expected action " << MotionEvent::actionToString(action)
- << ", but got " << MotionEvent::actionToString(arg.getAction());
- }
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "downTime should match eventTime for ACTION_DOWN events";
- matches &= arg.getDownTime() == arg.getEventTime();
- }
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
-}
-
MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
return arg.getDownTime() == downTime;
}
-MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
- return arg.getDisplayId() == displayId;
-}
-
-MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
- return arg.getDeviceId() == deviceId;
-}
-
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.getSource());
@@ -206,11 +193,9 @@
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
- InputDispatcherConfiguration mConfig;
-
struct AnrResult {
sp<IBinder> token{};
- gui::Pid pid{-1};
+ gui::Pid pid{gui::Pid::INVALID};
};
public:
@@ -349,11 +334,6 @@
"signal";
}
- void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
- mConfig.keyRepeatTimeout = timeout;
- mConfig.keyRepeatDelay = delay;
- }
-
PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -550,8 +530,6 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
-
bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
switch (inputEvent.getType()) {
@@ -650,7 +628,7 @@
void SetUp() override {
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
- mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
}
@@ -691,7 +669,7 @@
// Rejects undefined key actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
INVALID_HMAC,
- /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+ /*action=*/-1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
@@ -727,11 +705,11 @@
ui::Transform identityTransform;
// Rejects undefined motion actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+ /*action=*/-1, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -743,7 +721,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -755,7 +733,7 @@
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -767,7 +745,7 @@
0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -779,7 +757,7 @@
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -791,7 +769,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 0, pointerProperties, pointerCoords);
+ /*pointerCount=*/0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -802,7 +780,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -815,7 +793,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -827,7 +805,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -841,7 +819,7 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
ARBITRARY_TIME,
- /*pointerCount*/ 2, pointerProperties, pointerCoords);
+ /*pointerCount=*/2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -870,7 +848,6 @@
namespace {
-// --- InputDispatcherTest SetInputWindowTest ---
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
@@ -902,9 +879,9 @@
mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
}
- InputEvent* consume() {
+ InputEvent* consume(std::chrono::milliseconds timeout) {
InputEvent* event;
- std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+ std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
if (!consumeSeq) {
return nullptr;
}
@@ -916,7 +893,8 @@
* Receive an event without acknowledging it.
* Return the sequence number that could later be used to send finished signal.
*/
- std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
+ std::optional<uint32_t> receiveEvent(std::chrono::milliseconds timeout,
+ InputEvent** outEvent = nullptr) {
uint32_t consumeSeq;
InputEvent* event;
@@ -926,7 +904,7 @@
status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
&event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > 100ms) {
+ if (elapsed > timeout) {
break;
}
}
@@ -966,7 +944,7 @@
void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
@@ -1012,7 +990,7 @@
}
MotionEvent* consumeMotion() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
@@ -1033,7 +1011,7 @@
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::FOCUS, event->getType())
@@ -1047,7 +1025,7 @@
}
void consumeCaptureEvent(bool hasCapture) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::CAPTURE, event->getType())
@@ -1061,7 +1039,7 @@
}
void consumeDragEvent(bool isExiting, float x, float y) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
@@ -1076,7 +1054,7 @@
}
void consumeTouchModeEvent(bool inTouchMode) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
@@ -1089,7 +1067,7 @@
}
void assertNoEvents() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
if (event == nullptr) {
return;
}
@@ -1176,23 +1154,6 @@
return handle;
}
- /**
- * This is different from clone, because clone will make a "mirror" window - a window with the
- * same token, but a different ID. The original window and the clone window are allowed to be
- * sent to the dispatcher at the same time - they can coexist inside the dispatcher.
- * This function will create a different object of WindowInfoHandle, but with the same
- * properties as the original object - including the ID.
- * You can use either the old or the new object to consume the events.
- * IMPORTANT: The duplicated object is supposed to replace the original object, and not appear
- * at the same time inside dispatcher.
- */
- sp<FakeWindowHandle> duplicate() {
- sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mName);
- handle->mInfo = mInfo;
- handle->mInputReceiver = mInputReceiver;
- return handle;
- }
-
void setTouchable(bool touchable) {
mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
}
@@ -1289,6 +1250,25 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+ KeyEvent* consumeKey() {
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed : no event";
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::KEY) {
+ ADD_FAILURE() << "Instead of key event, got " << *event;
+ return nullptr;
+ }
+ return static_cast<KeyEvent*>(event);
+ }
+
+ void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
+ KeyEvent* keyEvent = consumeKey();
+ ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher;
+ ASSERT_THAT(*keyEvent, matcher);
+ }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags);
}
@@ -1349,13 +1329,11 @@
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- InputEvent* event = consume();
- ASSERT_NE(nullptr, event);
- ASSERT_EQ(InputEventType::MOTION, event->getType());
- const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
- EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getX());
- EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getY());
+ MotionEvent* motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked());
+ EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX());
+ EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY());
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
@@ -1399,7 +1377,7 @@
ADD_FAILURE() << "Invalid receive event on window with no receiver";
return std::nullopt;
}
- return mInputReceiver->receiveEvent(outEvent);
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED, outEvent);
}
void finishEvent(uint32_t sequenceNum) {
@@ -1412,15 +1390,15 @@
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume() {
+ InputEvent* consume(std::chrono::milliseconds timeout) {
if (mInputReceiver == nullptr) {
return nullptr;
}
- return mInputReceiver->consume();
+ return mInputReceiver->consume(timeout);
}
MotionEvent* consumeMotion() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << "Consume failed : no event";
return nullptr;
@@ -1468,7 +1446,7 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
static InputEventInjectionResult injectKey(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1486,10 +1464,19 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
+ return dispatcher.injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
}
-static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
+static void assertInjectedKeyTimesOut(InputDispatcher& dispatcher) {
+ InputEventInjectionResult result =
+ injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ InputEventInjectionSync::WAIT_FOR_RESULT, CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
+ if (result != InputEventInjectionResult::TIMED_OUT) {
+ FAIL() << "Injection should have timed out, but got " << ftl::enum_string(result);
+ }
+}
+
+static InputEventInjectionResult injectKeyDown(InputDispatcher& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId);
}
@@ -1497,30 +1484,30 @@
// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
// has to be woken up to process the repeating key.
-static InputEventInjectionResult injectKeyDownNoRepeat(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDownNoRepeat(InputDispatcher& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId,
InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
/*allowKeyRepeat=*/false);
}
-static InputEventInjectionResult injectKeyUp(const std::unique_ptr<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyUp(InputDispatcher& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, displayId);
}
static InputEventInjectionResult injectMotionEvent(
- const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
+ InputDispatcher& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::optional<gui::Uid> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
- return dispatcher->injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
- policyFlags);
+ return dispatcher.injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
+ policyFlags);
}
static InputEventInjectionResult injectMotionEvent(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source,
- int32_t displayId, const PointF& position = {100, 200},
+ InputDispatcher& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ const PointF& position = {100, 200},
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1544,14 +1531,14 @@
targetUid, policyFlags);
}
-static InputEventInjectionResult injectMotionDown(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId,
- const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(InputDispatcher& dispatcher, int32_t source,
+ int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
-static InputEventInjectionResult injectMotionUp(const std::unique_ptr<InputDispatcher>& dispatcher,
- int32_t source, int32_t displayId,
+static InputEventInjectionResult injectMotionUp(InputDispatcher& dispatcher, int32_t source,
+ int32_t displayId,
const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
@@ -1560,8 +1547,8 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A,
- KEY_A, AMETA_NONE, currentTime);
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A,
+ AMETA_NONE, currentTime);
return args;
}
@@ -1571,7 +1558,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /* flags */ 0, AKEYCODE_C, KEY_C, AMETA_META_ON,
+ displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON,
currentTime);
return args;
@@ -1582,7 +1569,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /* flags */ 0, AKEYCODE_ASSIST, KEY_ASSISTANT,
+ displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT,
AMETA_NONE, currentTime);
return args;
@@ -1612,12 +1599,12 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId,
- POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
- AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0,
+ AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
- pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+ pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /*videoFrames=*/{});
return args;
}
@@ -1647,7 +1634,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher,
"Window that breaks its input channel", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Window closes its channel, but the window remains.
window->destroyReceiver();
@@ -1659,9 +1646,9 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1673,10 +1660,10 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Inject a MotionEvent to an unknown display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1684,8 +1671,8 @@
}
/**
- * Calling setInputWindows once should not cause any issues.
- * This test serves as a sanity check for the next test, where setInputWindows is
+ * Calling onWindowInfosChanged once should not cause any issues.
+ * This test serves as a sanity check for the next test, where onWindowInfosChanged is
* called twice.
*/
TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) {
@@ -1694,9 +1681,9 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1705,7 +1692,7 @@
}
/**
- * Calling setInputWindows twice, with the same info, should not cause any issues.
+ * Calling onWindowInfosChanged twice, with the same info, should not cause any issues.
*/
TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -1713,10 +1700,10 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1732,9 +1719,10 @@
sp<FakeWindowHandle> windowSecond =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
@@ -1758,9 +1746,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1769,7 +1758,7 @@
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1777,7 +1766,7 @@
wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
// Now the foreground window goes away, but the wallpaper stays
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged({{*wallpaperWindow->getInfo()}, {}, 0, 0});
foregroundWindow->consumeMotionCancel();
// Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
@@ -1793,7 +1782,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// First touch pointer down on right window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
@@ -1813,7 +1802,7 @@
window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
// Remove the window. The gesture should be canceled
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
const std::map<int32_t, PointF> expectedPointers{{1, PointF{110, 100}}};
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedPointers)));
@@ -1836,9 +1825,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1847,7 +1837,7 @@
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1860,7 +1850,7 @@
// Now the foreground window goes away, but the wallpaper stays, even though its channel
// is no longer valid.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged({{*wallpaperWindow->getInfo()}, {}, 0, 0});
foregroundWindow->consumeMotionCancel();
}
@@ -1884,11 +1874,12 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
// Touch down on top window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1904,7 +1895,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1920,20 +1911,19 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionPointerUp(0);
wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.displayId(ADISPLAY_ID_DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
- .pointer(PointerBuilder(/* id */ 1,
- ToolType::FINGER)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
.x(100)
.y(100))
.build(),
@@ -1968,12 +1958,15 @@
wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo(), *wallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1989,7 +1982,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(300).y(100))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2000,7 +1993,8 @@
expectedWallpaperFlags);
// Now, leftWindow, which received the first finger, disappears.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
leftWindow->consumeMotionCancel();
// Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
@@ -2014,7 +2008,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(310).y(110))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
rightWindow->consumeMotionMove();
@@ -2044,12 +2038,15 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo(), *wallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2059,7 +2056,7 @@
// Move to right window, the left window should receive cancel.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {201, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2096,7 +2093,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
// Two pointers down
@@ -2169,7 +2166,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
// All times need to start at the current time, otherwise the dispatcher will drop the events as
// stale.
const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -2177,86 +2175,74 @@
const int32_t touchDeviceId = 4;
// Move the cursor from right
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
.downTime(baseTime + 10)
.eventTime(baseTime + 20)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// .. to the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
.downTime(baseTime + 10)
.eventTime(baseTime + 30)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(110)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Now tap the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 40)
.eventTime(baseTime + 40)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// release tap
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 40)
.eventTime(baseTime + 50)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
// Tap the window on the right
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 60)
.eventTime(baseTime + 60)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// release tap
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 60)
.eventTime(baseTime + 70)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
@@ -2281,7 +2267,7 @@
window->setFrame(Rect(0, 0, 200, 200));
// Only a single window is present at first
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Start hovering in the window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2300,7 +2286,8 @@
obscuringWindow->setNoInputChannel(true);
obscuringWindow->setFocusable(false);
obscuringWindow->setAlpha(1.0);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// While this new obscuring window is present, the hovering is stopped
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
@@ -2309,7 +2296,7 @@
window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
// Now the obscuring window goes away.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// And a new hover gesture starts.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2329,7 +2316,7 @@
window->setFrame(Rect(0, 0, 200, 200));
// Only a single window is present at first
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Start hovering in the window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2348,7 +2335,8 @@
obscuringWindow->setNoInputChannel(true);
obscuringWindow->setFocusable(false);
obscuringWindow->setAlpha(1.0);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// While this new obscuring window is present, the hovering continues. The event can't go to the
// bottom window due to obstructed touches, so it should generate HOVER_EXIT for that window.
@@ -2359,7 +2347,7 @@
window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
// Now the obscuring window goes away.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Hovering continues in the same position. The hovering pointer re-enters the bottom window,
// so it should generate a HOVER_ENTER
@@ -2392,11 +2380,11 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
const int32_t mouseDeviceId = 6;
- NotifyMotionArgs args;
// Start hovering over the left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
@@ -2468,11 +2456,10 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
const int32_t mouseDeviceId = 6;
- NotifyMotionArgs args;
// First touch pointer down
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -2535,19 +2522,16 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
- NotifyMotionArgs args;
// Pretend a test injects an ACTION_DOWN mouse event, but forgets to lift up the touch after
// completion.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
.build()));
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
@@ -2588,41 +2572,36 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
const int32_t mouseDeviceId = 6;
const int32_t touchDeviceId = 4;
// Hover over the left window. Keep the cursor there.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Tap on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
@@ -2630,27 +2609,21 @@
// First finger down on right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// Second finger down on the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
- .pointer(PointerBuilder(1, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
@@ -2671,69 +2644,59 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t stylusDeviceId = 5;
const int32_t touchDeviceId = 4;
// Start hovering with stylus
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Finger down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// Try to continue hovering with stylus. Since we are already down, injection should fail
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
.build()));
// No event should be sent. This event should be ignored because a pointer from another device
// is already down.
// Lift up the finger
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
// Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// No more events
@@ -2757,7 +2720,7 @@
window->setNoInputChannel(true);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Start hovering with stylus
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
@@ -2815,7 +2778,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
const int32_t mouseDeviceId = 7;
const int32_t touchDeviceId = 4;
@@ -2910,7 +2873,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", DISPLAY_ID);
- mDispatcher->setInputWindows({{DISPLAY_ID, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Touch down on the empty space
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{-1, -1}}));
@@ -2938,7 +2901,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
window2->setTouchableRegion(Region{{100, 0, 200, 100}});
- mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
// Touch down on the non-touchable window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
@@ -2966,28 +2929,27 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
window2->setTouchableRegion(Region{{100, 0, 200, 100}});
- mDispatcher->setInputWindows({{DISPLAY_ID, {window1, window2}}});
+ mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
// Touch down on the first window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
mDispatcher->waitForIdle();
- InputEvent* inputEvent1 = window1->consume();
- ASSERT_NE(inputEvent1, nullptr);
+
+ MotionEvent* motionEvent1 = window1->consumeMotion();
+ ASSERT_NE(motionEvent1, nullptr);
window2->assertNoEvents();
- MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
- nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
- ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
+ nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
+ ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- InputEvent* inputEvent2 = window2->consume();
- ASSERT_NE(inputEvent2, nullptr);
- MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
- nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
+ MotionEvent* motionEvent2 = window2->consumeMotion();
+ ASSERT_NE(motionEvent2, nullptr);
+ nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
+ ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
@@ -3026,11 +2988,12 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
// Start cursor position in right window so that we can move the cursor to left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(900).y(400))
@@ -3039,7 +3002,7 @@
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3049,7 +3012,7 @@
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3058,7 +3021,7 @@
windowLeft->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -3068,7 +3031,7 @@
windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -3078,7 +3041,7 @@
windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3087,7 +3050,7 @@
// Move mouse cursor back to right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(900).y(400))
@@ -3110,7 +3073,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 600, 800));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t touchDeviceId = 4;
const int32_t mouseDeviceId = 6;
@@ -3171,16 +3134,14 @@
window->setFrame(Rect(0, 0, 600, 800));
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Send mouse cursor to the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
@@ -3205,26 +3166,22 @@
window->setFrame(Rect(0, 0, 600, 800));
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Send mouse cursor to the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
.build()));
// Move mouse cursor
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(110)
- .y(110))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
@@ -3237,13 +3194,11 @@
WithSource(AINPUT_SOURCE_MOUSE)));
// Touch down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(200)
- .y(200))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
WithSource(AINPUT_SOURCE_MOUSE)));
@@ -3261,13 +3216,11 @@
// Touch UP on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(200)
- .y(200))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
.build()));
spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3278,13 +3231,11 @@
// One more tap - DOWN
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(250)
- .y(250))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3293,13 +3244,11 @@
// Touch UP on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(250)
- .y(250))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3320,10 +3269,10 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3331,7 +3280,7 @@
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3340,7 +3289,7 @@
window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -3350,7 +3299,7 @@
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -3360,7 +3309,7 @@
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3370,7 +3319,7 @@
// We already canceled the hovering implicitly by injecting the "DOWN" event without lifting the
// hover first. Therefore, injection of HOVER_EXIT is inconsistent, and should fail.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3390,20 +3339,18 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(400))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Remove the window, but keep the channel.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
}
@@ -3416,7 +3363,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const int32_t mouseDeviceId = 7;
const int32_t touchDeviceId = 4;
@@ -3451,7 +3398,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Inject a hover_move from mouse.
NotifyMotionArgs motionArgs =
@@ -3494,40 +3441,36 @@
SECOND_DISPLAY_ID);
windowSecondDisplay->setFrame(Rect(0, 0, 600, 800));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
- {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowDefaultDisplay->getInfo(), *windowSecondDisplay->getInfo()}, {}, 0, 0});
// Set cursor position in window in default display and check that hover enter and move
// events are generated.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.displayId(ADISPLAY_ID_DEFAULT)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(600))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(600))
.build()));
windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Remove all windows in secondary display and check that no event happens on window in
// primary display.
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
+ mDispatcher->onWindowInfosChanged({{*windowDefaultDisplay->getInfo()}, {}, 0, 0});
+
windowDefaultDisplay->assertNoEvents();
// Move cursor position in window in default display and check that only hover move
// event is generated and not hover enter event.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
- {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowDefaultDisplay->getInfo(), *windowSecondDisplay->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.displayId(ADISPLAY_ID_DEFAULT)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(400)
- .y(700))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(400).y(700))
.build()));
windowDefaultDisplay->consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
@@ -3547,12 +3490,13 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowRight->assertNoEvents();
@@ -3564,7 +3508,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3586,7 +3530,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
@@ -3606,7 +3550,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(10))
@@ -3631,7 +3575,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3656,7 +3600,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3691,7 +3635,7 @@
outsideWindow->setFrame(Rect{100, 100, 200, 200});
outsideWindow->setWatchOutsideTouch(true);
// outsideWindow must be above 'window' to receive ACTION_OUTSIDE events when 'window' is tapped
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {outsideWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*outsideWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Tap on first window.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -3724,7 +3668,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Third Window",
ADISPLAY_ID_DEFAULT);
thirdWindow->setFrame(Rect{200, 200, 300, 300});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, secondWindow, thirdWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *secondWindow->getInfo(), *thirdWindow->getInfo()}, {}, 0, 0});
// First pointer lands outside all windows. `window` does not get ACTION_OUTSIDE.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -3793,7 +3738,7 @@
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -3806,7 +3751,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -3818,6 +3763,44 @@
EXPECT_EQ(-10, event->getY(1)); // -50 + 40
}
+/**
+ * Two windows: a splittable and a non-splittable.
+ * The non-splittable window shouldn't receive any "incomplete" gestures.
+ * Send the first pointer to the splittable window, and then touch the non-splittable window.
+ * The second pointer should be dropped because the initial window is splittable, so it won't get
+ * any pointers outside of it, and the second window is non-splittable, so it shouldn't get any
+ * "incomplete" gestures.
+ */
+TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setPreventSplitting(false);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right non-splittable Window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setPreventSplitting(true);
+ rightWindow->setFrame(Rect(100, 100, 200, 200));
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ // Touch down on left, splittable window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeOnlySentToTrustedOverlays) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -3829,7 +3812,7 @@
trustedOverlay->setSpy(true);
trustedOverlay->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {trustedOverlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*trustedOverlay->getInfo(), *window->getInfo()}, {}, 0, 0});
// Start a three-finger touchpad swipe
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
@@ -3891,7 +3874,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Start a three-finger touchpad swipe
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
@@ -3941,16 +3924,14 @@
/**
* Send a two-pointer gesture to a single window. The window's orientation changes in response to
* the first pointer.
- * Ensure that the second pointer is not sent to the window.
- *
- * The subsequent gesture should be correctly delivered to the window.
+ * Ensure that the second pointer and the subsequent gesture is correctly delivered to the window.
*/
TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -3961,19 +3942,10 @@
window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
- // We need a new window object for the same window, because dispatcher will store objects by
- // reference. That means that the testing code and the dispatcher will refer to the same shared
- // object. Calling window->setTransform here would affect dispatcher's comparison
- // of the old window to the new window, since both the old window and the new window would be
- // updated to the same value.
- sp<FakeWindowHandle> windowDup = window->duplicate();
-
// Change the transform so that the orientation is now different from original.
- windowDup->setWindowTransform(0, -1, 1, 0);
+ window->setWindowTransform(0, -1, 1, 0);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDup}}});
-
- window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.downTime(baseTime + 10)
@@ -3982,26 +3954,33 @@
.pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
.build());
- // Finish the gesture and start a new one. Ensure the new gesture is sent to the window
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+ // Finish the gesture and start a new one. Ensure all events are sent to the window.
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
.downTime(baseTime + 10)
.eventTime(baseTime + 40)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
.build());
+
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_UP));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.downTime(baseTime + 10)
.eventTime(baseTime + 50)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.downTime(baseTime + 60)
.eventTime(baseTime + 60)
.pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
.build());
- windowDup->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
}
/**
@@ -4088,7 +4067,7 @@
// Send down to the first window. The point is represented in the logical display space. The
// point is selected so that if the hit test was done in logical display space, then it would
// end up in the incorrect window.
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
PointF{75 * 2, 55 * 4});
firstWindow->consumeMotionDown();
@@ -4115,7 +4094,7 @@
.build();
event.transform(matrix);
- injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, event, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT);
firstWindow->consumeMotionDown();
@@ -4242,7 +4221,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaper->setIsWallpaper(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4297,7 +4277,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4340,7 +4321,8 @@
secondWindow->setPreventSplitting(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4402,8 +4384,11 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT);
wallpaper2->setIsWallpaper(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}});
+ mDispatcher->onWindowInfosChanged({{*firstWindow->getInfo(), *wallpaper1->getInfo(),
+ *secondWindow->getInfo(), *wallpaper2->getInfo()},
+ {},
+ 0,
+ 0});
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -4466,7 +4451,8 @@
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -4527,7 +4513,8 @@
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -4595,13 +4582,16 @@
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
// Update window info, let it find window handle of second display first.
- mDispatcher->setInputWindows(
- {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
- {ADISPLAY_ID_DEFAULT,
- {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindowInSecondary->getInfo(), *secondWindowInSecondary->getInfo(),
+ *mirrorWindowInPrimary->getInfo(), *firstWindowInPrimary->getInfo(),
+ *secondWindowInPrimary->getInfo()},
+ {},
+ 0,
+ 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4616,14 +4606,14 @@
secondWindowInPrimary->consumeMotionDown();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionMove();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
@@ -4651,14 +4641,18 @@
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
// Update window info, let it find window handle of second display first.
- mDispatcher->setInputWindows(
- {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
- {ADISPLAY_ID_DEFAULT,
- {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindowInSecondary->getInfo(), *secondWindowInSecondary->getInfo(),
+ *mirrorWindowInPrimary->getInfo(), *firstWindowInPrimary->getInfo(),
+ *secondWindowInPrimary->getInfo()},
+ {},
+ 0,
+ 0});
// Touch on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
+ {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -4672,14 +4666,14 @@
secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
@@ -4691,7 +4685,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4713,7 +4707,7 @@
window->setDisableUserActivity(true);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4734,7 +4728,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4755,7 +4749,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4777,7 +4771,7 @@
window->setDisableUserActivity(true);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -4797,10 +4791,10 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4817,7 +4811,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
mDispatcher->waitForIdle();
@@ -4831,7 +4825,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Send key
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
@@ -4858,7 +4852,8 @@
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*firstWindow->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
PointF pointInFirst = {300, 200};
PointF pointInSecond = {300, 600};
@@ -4902,7 +4897,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
@@ -4928,7 +4923,9 @@
expectedFlags);
}
- std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+ std::optional<int32_t> receiveEvent() {
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ }
void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
@@ -4961,18 +4958,7 @@
/*expectedFlags=*/0);
}
- MotionEvent* consumeMotion() {
- InputEvent* event = mInputReceiver->consume();
- if (!event) {
- ADD_FAILURE() << "No event was produced";
- return nullptr;
- }
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << "Expected MotionEvent, got " << *event;
- return nullptr;
- }
- return static_cast<MotionEvent*>(event);
- }
+ MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
void assertNoEvents() { mInputReceiver->assertNoEvents(); }
@@ -4997,9 +4983,9 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -5008,7 +4994,7 @@
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -5016,14 +5002,14 @@
monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
// Now the foreground window goes away
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
window->consumeMotionCancel();
monitor.assertNoEvents(); // Global monitor does not get a cancel yet
// If more events come in, there will be no more foreground window to send them to. This will
// cause a cancel for the monitor, as well.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {120, 200}))
<< "Injection should fail because the window was removed";
window->assertNoEvents();
@@ -5035,12 +5021,12 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5052,10 +5038,10 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5065,7 +5051,7 @@
EXPECT_NE(OK, mDispatcher->pilferPointers(monitor.getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -5077,14 +5063,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->setWindowOffset(20, 40);
window->setWindowTransform(0, 1, -1, 0);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
MotionEvent* event = monitor.consumeMotion();
@@ -5097,7 +5083,7 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Injection should fail if there is a monitor, but no touchable window";
monitor.assertNoEvents();
}
@@ -5107,7 +5093,7 @@
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Fake Window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -5144,13 +5130,13 @@
window->setFocusable(true);
SCOPED_TRACE("Check default value of touch mode");
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
SCOPED_TRACE("Remove the window to trigger focus loss");
window->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->consumeFocusEvent(/*hasFocus=*/false, /*inTouchMode=*/true);
SCOPED_TRACE("Disable touch mode");
@@ -5158,13 +5144,13 @@
/*hasPermission=*/true, ADISPLAY_ID_DEFAULT);
window->consumeTouchModeEvent(false);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/false);
SCOPED_TRACE("Remove the window to trigger focus loss");
window->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->consumeFocusEvent(/*hasFocus=*/false, /*inTouchMode=*/false);
SCOPED_TRACE("Enable touch mode again");
@@ -5172,7 +5158,7 @@
/*hasPermission=*/true, ADISPLAY_ID_DEFAULT);
window->consumeTouchModeEvent(true);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -5187,7 +5173,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -5195,7 +5181,7 @@
const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
mDispatcher->notifyKey(keyArgs);
- InputEvent* event = window->consume();
+ KeyEvent* event = window->consumeKey();
ASSERT_NE(event, nullptr);
std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
@@ -5239,7 +5225,7 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(motionArgs);
- InputEvent* event = window->consume();
+ MotionEvent* event = window->consumeMotion();
ASSERT_NE(event, nullptr);
std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
@@ -5333,11 +5319,12 @@
// Top window is also focusable but is not granted focus.
windowTop->setFocusable(true);
windowSecond->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
setFocusedWindow(windowSecond);
windowSecond->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
@@ -5354,12 +5341,11 @@
window->setFocusable(true);
// Release channel for window is no longer valid.
window->releaseChannel();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
// Test inject a key down, should timeout.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
// window channel is invalid, so it should not receive any input event.
window->assertNoEvents();
@@ -5372,12 +5358,11 @@
window->setFocusable(false);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
// Test inject a key down, should timeout.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
// window is not focusable, so it should not receive any input event.
window->assertNoEvents();
@@ -5393,16 +5378,18 @@
windowTop->setFocusable(true);
windowSecond->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
setFocusedWindow(windowTop);
windowTop->consumeFocusEvent(true);
windowTop->editInfo()->focusTransferTarget = windowSecond->getToken();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
windowSecond->consumeFocusEvent(true);
windowTop->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
@@ -5420,11 +5407,12 @@
windowTop->setFocusable(true);
windowSecond->setFocusable(false);
windowTop->editInfo()->focusTransferTarget = windowSecond->getToken();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
setFocusedWindow(windowTop);
windowTop->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Event should be dropped.
@@ -5444,7 +5432,8 @@
window->setFocusable(true);
previousFocusedWindow->setFocusable(true);
window->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, previousFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *previousFocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(previousFocusedWindow);
previousFocusedWindow->consumeFocusEvent(true);
@@ -5454,15 +5443,15 @@
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// Window does not get focus event or key down.
window->assertNoEvents();
// Window becomes visible.
window->setVisible(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Window receives focus event.
window->consumeFocusEvent(true);
@@ -5478,7 +5467,7 @@
// window is granted focus.
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -5530,8 +5519,8 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
slipperyExitWindow->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*slipperyExitWindow->getInfo(), *slipperyEnterWindow->getInfo()}, {}, 0, 0});
// Use notifyMotion instead of injecting to avoid dealing with injection permissions
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -5539,8 +5528,8 @@
{{50, 50}}));
slipperyExitWindow->consumeMotionDown();
slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*slipperyExitWindow->getInfo(), *slipperyEnterWindow->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -5572,8 +5561,8 @@
rightDropTouchesWindow->setFrame(Rect(100, 0, 200, 100));
rightDropTouchesWindow->setDropInput(true);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {leftSlipperyWindow, rightDropTouchesWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*leftSlipperyWindow->getInfo(), *rightDropTouchesWindow->getInfo()}, {}, 0, 0});
// Start touch in the left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5613,7 +5602,8 @@
rightWindow->setFrame(Rect(100, 0, 200, 100));
rightWindow->setOwnerInfo(gui::Pid{3}, Uid{103});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightSpy, rightWindow, leftWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*rightSpy->getInfo(), *rightWindow->getInfo(), *leftWindow->getInfo()}, {}, 0, 0});
// Touch in the left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5678,7 +5668,7 @@
window->setFrame(Rect(0, 0, 100, 100));
window->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
ASSERT_NO_FATAL_FAILURE(window->consumeFocusEvent(true));
@@ -5705,10 +5695,9 @@
virtual void SetUp() override {
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
- mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
- mDispatcher->requestRefreshConfiguration();
- mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
ASSERT_EQ(OK, mDispatcher->start());
setUpWindow();
@@ -5719,7 +5708,7 @@
mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -5736,15 +5725,8 @@
void expectKeyRepeatOnce(int32_t repeatCount) {
SCOPED_TRACE(StringPrintf("Checking event with repeat count %" PRId32, repeatCount));
- InputEvent* repeatEvent = mWindow->consume();
- ASSERT_NE(nullptr, repeatEvent);
-
- ASSERT_EQ(InputEventType::KEY, repeatEvent->getType());
-
- KeyEvent* repeatKeyEvent = static_cast<KeyEvent*>(repeatEvent);
- uint32_t eventAction = repeatKeyEvent->getAction();
- EXPECT_EQ(AKEY_EVENT_ACTION_DOWN, eventAction);
- EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
+ mWindow->consumeKeyEvent(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithRepeatCount(repeatCount)));
}
void sendAndConsumeKeyUp(int32_t deviceId) {
@@ -5824,7 +5806,7 @@
GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- InputEvent* repeatEvent = mWindow->consume();
+ KeyEvent* repeatEvent = mWindow->consumeKey();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
IdGenerator::getSource(repeatEvent->getId()));
@@ -5837,7 +5819,7 @@
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- InputEvent* repeatEvent = mWindow->consume();
+ KeyEvent* repeatEvent = mWindow->consumeKey();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
int32_t id = repeatEvent->getId();
EXPECT_EQ(idSet.end(), idSet.find(id));
@@ -5858,7 +5840,8 @@
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
windowInPrimary->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
+ mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
+
setFocusedWindow(windowInPrimary);
windowInPrimary->consumeFocusEvent(true);
@@ -5871,7 +5854,8 @@
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocusable(true);
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowInPrimary->getInfo(), *windowInSecondary->getInfo()}, {}, 0, 0});
setFocusedWindow(windowInSecondary);
windowInSecondary->consumeFocusEvent(true);
}
@@ -5895,14 +5879,14 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
@@ -5911,27 +5895,26 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
// Remove all windows in secondary display.
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
+ mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
// Old focus should receive a cancel event.
windowInSecondary->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
windowInPrimary->assertNoEvents();
windowInSecondary->consumeFocusEvent(false);
windowInSecondary->assertNoEvents();
@@ -5946,7 +5929,7 @@
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5955,7 +5938,7 @@
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -5964,7 +5947,7 @@
// Lift up the touch from the second display
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionUp(SECOND_DISPLAY_ID);
@@ -5973,7 +5956,7 @@
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -5990,7 +5973,7 @@
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -6002,13 +5985,19 @@
sp<FakeWindowHandle> secondWindowInPrimary =
sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
secondWindowInPrimary->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*windowInPrimary->getInfo(), *secondWindowInPrimary->getInfo(),
+ *windowInSecondary->getInfo()},
+ {},
+ 0,
+ 0});
setFocusedWindow(secondWindowInPrimary);
windowInPrimary->consumeFocusEvent(false);
secondWindowInPrimary->consumeFocusEvent(true);
// Test inject a key down.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->assertNoEvents();
@@ -6023,14 +6012,14 @@
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
@@ -6044,14 +6033,14 @@
// Test inject a move motion event, no window/monitor should receive the event.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInSecondary->assertNoEvents();
@@ -6099,36 +6088,36 @@
// Test InputFilter for MotionEvent
TEST_F(InputFilterTest, MotionEvent_InputFilter) {
// Since the InputFilter is disabled by default, check if touch events aren't filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
// Test touch on both primary and second display, and check if both events are filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true);
// Disable InputFilter
mDispatcher->setInputFilterEnabled(false);
// Test touch on both primary and second display, and check if both events aren't filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
}
// Test InputFilter for KeyEvent
TEST_F(InputFilterTest, KeyEvent_InputFilter) {
// Since the InputFilter is disabled by default, check if key event aren't filtered.
- testNotifyKey(/*expectToBeFiltered*/ false);
+ testNotifyKey(/*expectToBeFiltered=*/false);
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
// Send a key event, and check if it is filtered.
- testNotifyKey(/*expectToBeFiltered*/ true);
+ testNotifyKey(/*expectToBeFiltered=*/true);
// Disable InputFilter
mDispatcher->setInputFilterEnabled(false);
// Send a key event, and check if it isn't filtered.
- testNotifyKey(/*expectToBeFiltered*/ false);
+ testNotifyKey(/*expectToBeFiltered=*/false);
}
// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
@@ -6151,8 +6140,8 @@
mDispatcher->setInputFilterEnabled(true);
// Ensure the correct transforms are used for the displays.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
- testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true, firstDisplayTransform);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true, secondDisplayTransform);
}
class InputFilterInjectionPolicyTest : public InputDispatcherTest {
@@ -6174,7 +6163,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
mWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -6191,15 +6180,10 @@
POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
policyFlags | additionalPolicyFlags));
- InputEvent* received = mWindow->consume();
- ASSERT_NE(nullptr, received);
- ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
- ASSERT_EQ(received->getType(), InputEventType::KEY);
- KeyEvent& keyEvent = static_cast<KeyEvent&>(*received);
- ASSERT_EQ(flags, keyEvent.getFlags());
+ mWindow->consumeKeyEvent(AllOf(WithDeviceId(resolvedDeviceId), WithFlags(flags)));
}
void testInjectedMotion(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId,
@@ -6221,20 +6205,15 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime,
eventTime,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ /*pointerCount=*/1, pointerProperties, pointerCoords);
const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
policyFlags | additionalPolicyFlags));
- InputEvent* received = mWindow->consume();
- ASSERT_NE(nullptr, received);
- ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
- ASSERT_EQ(received->getType(), InputEventType::MOTION);
- MotionEvent& motionEvent = static_cast<MotionEvent&>(*received);
- ASSERT_EQ(flags, motionEvent.getFlags());
+ mWindow->consumeMotionEvent(AllOf(WithFlags(flags), WithDeviceId(resolvedDeviceId)));
}
private:
@@ -6286,7 +6265,8 @@
mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -6309,7 +6289,7 @@
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
@@ -6323,7 +6303,8 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT,
+ {20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6335,7 +6316,7 @@
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -6348,7 +6329,7 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_TOUCH_POINT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6366,7 +6347,7 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(20).y(20))
.addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
.build();
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, event))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
@@ -6392,7 +6373,7 @@
ADISPLAY_ID_DEFAULT, mWindow1->getToken());
mWindow2->setFrame(Rect(100, 100, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
}
protected:
@@ -6408,28 +6389,24 @@
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
const std::string name = window->getName();
- InputEvent* event = window->consume();
+ MotionEvent* motionEvent = window->consumeMotion();
- ASSERT_NE(nullptr, event) << name.c_str()
- << ": consumer should have returned non-NULL event.";
+ ASSERT_NE(nullptr, motionEvent)
+ << name.c_str() << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(InputEventType::MOTION, event->getType())
- << name.c_str() << ": expected MotionEvent, got " << *event;
-
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- assertMotionAction(expectedAction, motionEvent.getAction());
- ASSERT_EQ(points.size(), motionEvent.getPointerCount());
+ assertMotionAction(expectedAction, motionEvent->getAction());
+ ASSERT_EQ(points.size(), motionEvent->getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
float expectedY = points[i].y;
- EXPECT_EQ(expectedX, motionEvent.getX(i))
+ EXPECT_EQ(expectedX, motionEvent->getX(i))
<< "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getX(i);
- EXPECT_EQ(expectedY, motionEvent.getY(i))
+ << ", got " << motionEvent->getX(i);
+ EXPECT_EQ(expectedY, motionEvent->getY(i))
<< "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getY(i);
+ << ", got " << motionEvent->getY(i);
}
}
@@ -6461,6 +6438,7 @@
TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
// Set scale value for window2
mWindow2->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
PointF touchedPoint = {10, 10};
@@ -6477,12 +6455,14 @@
// Update the transform so rotation is set
mWindow2->setWindowTransform(0, -1, 1, 0);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
@@ -6500,12 +6480,14 @@
// Update the transform so rotation is set for Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
@@ -6531,6 +6513,7 @@
// Touch Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
@@ -6544,6 +6527,7 @@
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
mWindow1->setWindowScale(0.5f, 0.5f);
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
@@ -6570,7 +6554,7 @@
*/
TEST_F(InputDispatcherMultiWindowSameTokenTests, TouchDoesNotSlipEvenIfSlippery) {
mWindow1->setSlippery(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Touch down in window 1
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -6592,7 +6576,7 @@
* that the pointer is hovering over may have a different transform.
*/
TEST_F(InputDispatcherMultiWindowSameTokenTests, HoverIntoClone) {
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Start hover in window 1
mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN,
@@ -6614,17 +6598,17 @@
InputDispatcherTest::SetUp();
mApplication = std::make_shared<FakeApplicationHandle>();
- mApplication->setDispatchingTimeout(20ms);
+ mApplication->setDispatchingTimeout(100ms);
mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "TestWindow",
ADISPLAY_ID_DEFAULT);
mWindow->setFrame(Rect(0, 0, 30, 30));
- mWindow->setDispatchingTimeout(30ms);
+ mWindow->setDispatchingTimeout(100ms);
mWindow->setFocusable(true);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -6635,16 +6619,17 @@
}
protected:
+ static constexpr std::chrono::duration SPY_TIMEOUT = 200ms;
std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mWindow;
static constexpr PointF WINDOW_LOCATION = {20, 20};
void tapOnWindow() {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
}
@@ -6654,8 +6639,8 @@
spy->setTrustedOverlay(true);
spy->setFocusable(false);
spy->setSpy(true);
- spy->setDispatchingTimeout(30ms);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}});
+ spy->setDispatchingTimeout(SPY_TIMEOUT);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
return spy;
}
};
@@ -6671,7 +6656,7 @@
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -6679,12 +6664,12 @@
TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, CONSUME_TIMEOUT_EVENT_EXPECTED,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not go to window because we have no focused window.
@@ -6703,7 +6688,7 @@
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
@@ -6721,7 +6706,7 @@
// Send a key to the app and have the app not respond right away.
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6732,12 +6717,12 @@
// We have a focused application, but no focused window
TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
// taps on the window work as normal
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
mDispatcher->waitForIdle();
@@ -6747,8 +6732,8 @@
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, /*allowKeyRepeat=*/false);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
@@ -6761,7 +6746,7 @@
*/
TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
KeyEvent event;
@@ -6770,7 +6755,7 @@
// Define a valid key down event that is stale (too old).
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
+ INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /*flags=*/0, AKEYCODE_A, KEY_A,
AMETA_NONE, /*repeatCount=*/1, eventTime, eventTime);
const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
@@ -6791,15 +6776,15 @@
// Make sure that we don't notify policy twice about the same ANR.
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, /*allowKeyRepeat=*/false);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6817,20 +6802,17 @@
// We have a focused application, but no focused window
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
- const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
// Future focused events get dropped right away
- ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(*mDispatcher));
ASSERT_TRUE(mDispatcher->waitForIdle());
mWindow->assertNoEvents();
}
@@ -6846,14 +6828,14 @@
*/
TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// Now send ACTION_UP, with identical timestamp
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -6870,7 +6852,7 @@
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
mWindow->consumeMotionDown();
@@ -6892,9 +6874,9 @@
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT));
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher, ADISPLAY_ID_DEFAULT));
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6939,19 +6921,20 @@
}
TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
- mDispatcher->setMonitorDispatchingTimeoutForTest(30ms);
+ mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(30ms, monitor.getToken(), MONITOR_PID);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(SPY_TIMEOUT, monitor.getToken(),
+ MONITOR_PID);
monitor.finishEvent(*consumeSeq);
monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
@@ -6993,7 +6976,7 @@
// it.
TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -7013,19 +6996,19 @@
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
- * not to to the focused window until the motion is processed.
+ * not get delivered to the focused window until the motion is processed.
*
* Warning!!!
* This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
* and the injection timeout that we specify when injecting the key.
- * We must have the injection timeout (10ms) be smaller than
+ * We must have the injection timeout (100ms) be smaller than
* KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
*
* If that value changes, this test should also change.
*/
TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
tapOnWindow();
std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
@@ -7038,13 +7021,15 @@
// we will receive INJECTION_TIMED_OUT as the result.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
- std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR.
+ // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mWindow->assertNoEvents();
std::this_thread::sleep_for(500ms);
// if we wait long enough though, dispatcher will give up, and still send the key
@@ -7063,7 +7048,7 @@
TEST_F(InputDispatcherSingleWindowAnr,
PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
tapOnWindow();
std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
@@ -7073,11 +7058,13 @@
// Don't finish the events yet, and send a key
// Injection is async, so it will succeed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// At this point, key is still pending, and should not be sent to the application yet.
- std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // Make sure the `assertNoEvents` check doesn't take too long. It uses
+ // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mWindow->assertNoEvents();
// Now tap down again. It should cause the pending key to go to the focused window right away.
tapOnWindow();
@@ -7144,7 +7131,7 @@
InputDispatcherTest::SetUp();
mApplication = std::make_shared<FakeApplicationHandle>();
- mApplication->setDispatchingTimeout(10ms);
+ mApplication->setDispatchingTimeout(100ms);
mUnfocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Unfocused",
ADISPLAY_ID_DEFAULT);
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
@@ -7153,7 +7140,7 @@
mFocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Focused",
ADISPLAY_ID_DEFAULT);
- mFocusedWindow->setDispatchingTimeout(30ms);
+ mFocusedWindow->setDispatchingTimeout(100ms);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
// Set focused application.
@@ -7161,7 +7148,8 @@
mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -7188,10 +7176,10 @@
private:
void tap(const PointF& location) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
}
};
@@ -7200,7 +7188,7 @@
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -7211,7 +7199,7 @@
mFakePolicy->assertNotifyAnrWasNotCalled();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
@@ -7239,20 +7227,22 @@
// But we should receive ANR for both.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
// Set the timeout for unfocused window to match the focused window
- mUnfocusedWindow->setDispatchingTimeout(10ms);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mUnfocusedWindow->setDispatchingTimeout(
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- sp<IBinder> anrConnectionToken1, anrConnectionToken2;
- ASSERT_NO_FATAL_FAILURE(anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms));
- ASSERT_NO_FATAL_FAILURE(anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms));
-
// We don't know which window will ANR first. But both of them should happen eventually.
- ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
- mFocusedWindow->getToken() == anrConnectionToken2);
- ASSERT_TRUE(mUnfocusedWindow->getToken() == anrConnectionToken1 ||
- mUnfocusedWindow->getToken() == anrConnectionToken2);
+ std::array<sp<IBinder>, 2> anrConnectionTokens = {mFakePolicy->getUnresponsiveWindowToken(
+ mFocusedWindow->getDispatchingTimeout(
+ DISPATCHING_TIMEOUT)),
+ mFakePolicy->getUnresponsiveWindowToken(0ms)};
+
+ ASSERT_THAT(anrConnectionTokens,
+ ::testing::UnorderedElementsAre(testing::Eq(mFocusedWindow->getToken()),
+ testing::Eq(mUnfocusedWindow->getToken())));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -7261,15 +7251,13 @@
mFocusedWindow->consumeMotionUp();
mUnfocusedWindow->consumeMotionOutside();
- sp<IBinder> responsiveToken1, responsiveToken2;
- ASSERT_NO_FATAL_FAILURE(responsiveToken1 = mFakePolicy->getResponsiveWindowToken());
- ASSERT_NO_FATAL_FAILURE(responsiveToken2 = mFakePolicy->getResponsiveWindowToken());
+ std::array<sp<IBinder>, 2> responsiveTokens = {mFakePolicy->getResponsiveWindowToken(),
+ mFakePolicy->getResponsiveWindowToken()};
// Both applications should be marked as responsive, in any order
- ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
- mFocusedWindow->getToken() == responsiveToken2);
- ASSERT_TRUE(mUnfocusedWindow->getToken() == responsiveToken1 ||
- mUnfocusedWindow->getToken() == responsiveToken2);
+ ASSERT_THAT(responsiveTokens,
+ ::testing::UnorderedElementsAre(testing::Eq(mFocusedWindow->getToken()),
+ testing::Eq(mUnfocusedWindow->getToken())));
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -7292,10 +7280,10 @@
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
// Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
// valid touch target
@@ -7316,7 +7304,7 @@
// If you tap outside of all windows, there will not be ANR
TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
LOCATION_OUTSIDE_ALL_WINDOWS));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -7325,10 +7313,11 @@
// Since the focused window is paused, tapping on it should not produce any events
TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
mFocusedWindow->setPaused(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
@@ -7342,13 +7331,13 @@
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
- * not to to the focused window until the motion is processed.
+ * not get delivered to the focused window until the motion is processed.
* If a different window becomes focused at this time, the key should go to that window instead.
*
* Warning!!!
* This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
* and the injection timeout that we specify when injecting the key.
- * We must have the injection timeout (10ms) be smaller than
+ * We must have the injection timeout (100ms) be smaller than
* KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
*
* If that value changes, this test should also change.
@@ -7356,7 +7345,8 @@
TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
// Set a long ANR timeout to prevent it from triggering
mFocusedWindow->setDispatchingTimeout(2s);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
tapOnUnfocusedWindow();
std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
@@ -7368,18 +7358,21 @@
// window even if motions are still being processed.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not be sent to the window, yet, because the window is still processing events
- // and the key remains pending, waiting for the touch events to be processed
- std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // and the key remains pending, waiting for the touch events to be processed.
+ // Make sure `assertNoEvents` doesn't take too long. It uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED
+ // under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mFocusedWindow->assertNoEvents();
// Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
mFocusedWindow->setFocusable(false);
mUnfocusedWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mUnfocusedWindow);
// Focus events should precede the key events
@@ -7457,20 +7450,21 @@
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(60ms);
+ focusedApplication->setDispatchingTimeout(200ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
mFocusedWindow->consumeFocusEvent(false);
// Send a key. The ANR timer should start because there is no focused window.
// 'focusedApplication' will get blamed if this timer completes.
// Key will not be sent anywhere because we have no focused window. It will remain pending.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -7481,7 +7475,7 @@
// simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
// For this test, it means that the key would get delivered to the window once it becomes
// focused.
- std::this_thread::sleep_for(10ms);
+ std::this_thread::sleep_for(100ms);
// Touch unfocused window. This should force the pending key to get dropped.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -7492,7 +7486,8 @@
// process (== drop) the key event, and by that time, ANR will be raised.
// Set the focused window first.
mFocusedWindow->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mFocusedWindow->getInfo(), *mUnfocusedWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
// We do not call "setFocusedApplication" here, even though the newly focused window belongs
@@ -7529,7 +7524,8 @@
ADISPLAY_ID_DEFAULT);
mBottomWindow->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mNoInputWindow->getInfo(), *mBottomWindow->getInfo()}, {}, 0, 0});
}
protected:
@@ -7564,7 +7560,8 @@
mNoInputWindow->setNoInputChannel(true);
mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mNoInputWindow->getInfo(), *mBottomWindow->getInfo()}, {}, 0, 0});
PointF touchedPoint = {10, 10};
@@ -7591,7 +7588,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
mWindow->setFocusable(true);
mMirror->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
}
};
@@ -7601,7 +7598,7 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
}
@@ -7613,20 +7610,20 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mMirror->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
// window loses focus since one of the windows associated with the token in not focusable
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7638,30 +7635,30 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mMirror->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mWindow->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
// window loses focus only after all windows associated with the token become invisible.
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7672,28 +7669,28 @@
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
// single window is removed but the window token remains focused
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mMirror->getInfo()}, {}, 0, 0});
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
// Both windows are removed
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7703,16 +7700,16 @@
// Request focus on an invisible mirror.
mWindow->setVisible(false);
mMirror->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
setFocusedWindow(mMirror);
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
mMirror->setVisible(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
// window gets focused
mWindow->consumeFocusEvent(true);
@@ -7736,7 +7733,8 @@
mSecondWindow->setFocusable(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
@@ -7928,7 +7926,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7939,7 +7937,7 @@
WindowWithBlockUntrustedOcclusionModeWithOpacityBelowThreshold_BlocksTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.7f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7950,7 +7948,7 @@
WindowWithBlockUntrustedOcclusionMode_DoesNotReceiveTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7959,7 +7957,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7970,7 +7968,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
w->setFrame(Rect(0, 0, 50, 50));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch({PointF{100, 100}});
@@ -7980,7 +7978,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(TOUCHED_APP_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -7990,7 +7988,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8000,7 +7998,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_DoesNotReceiveTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8017,7 +8015,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
w->setWatchOutsideTouch(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8028,7 +8026,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
w->setWatchOutsideTouch(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8039,7 +8037,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8050,7 +8048,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
MAXIMUM_OBSCURING_OPACITY);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8061,7 +8059,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8076,7 +8074,8 @@
const sp<FakeWindowHandle>& w2 =
getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*w1->getInfo(), *w2->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8091,7 +8090,8 @@
const sp<FakeWindowHandle>& w2 =
getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
OPACITY_FAR_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*w1->getInfo(), *w2->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8106,7 +8106,8 @@
const sp<FakeWindowHandle>& wC =
getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wB->getInfo(), *wC->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8120,7 +8121,8 @@
const sp<FakeWindowHandle>& wC =
getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wB->getInfo(), *wC->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8135,7 +8137,8 @@
const sp<FakeWindowHandle>& wB =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wA->getInfo(), *wB->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8150,7 +8153,8 @@
const sp<FakeWindowHandle>& wB =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wA->getInfo(), *wB->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8161,7 +8165,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8171,7 +8175,7 @@
TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithBlockUntrustedMode_AllowsTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::BLOCK_UNTRUSTED);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8183,7 +8187,7 @@
mDispatcher->setMaximumObscuringOpacityForTouch(0.0f);
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.1f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8194,7 +8198,7 @@
mDispatcher->setMaximumObscuringOpacityForTouch(0.0f);
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.0f);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8207,7 +8211,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_ABOVE_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8222,7 +8226,8 @@
const sp<FakeWindowHandle>& w2 =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*w1->getInfo(), *w2->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8242,7 +8247,8 @@
const sp<FakeWindowHandle>& wC =
getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
OPACITY_BELOW_THRESHOLD);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*wB->getInfo(), *wC->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8258,7 +8264,7 @@
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
w->setApplicationToken(mTouchWindow->getApplicationToken());
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+ mDispatcher->onWindowInfosChanged({{*w->getInfo(), *mTouchWindow->getInfo()}, {}, 0, 0});
touch();
@@ -8292,40 +8298,44 @@
mSpyWindow->setFrame(Rect(0, 0, 200, 100));
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()},
+ {},
+ 0,
+ 0});
}
void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
switch (fromSource) {
case AINPUT_SOURCE_TOUCHSCREEN:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
break;
case AINPUT_SOURCE_STYLUS:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(
- mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_STYLUS)
- .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
- .build()));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_STYLUS)
+ .buttonState(
+ AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::STYLUS)
+ .x(50)
+ .y(50))
+ .build()));
break;
case AINPUT_SOURCE_MOUSE:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(
- mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
- .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
- .x(50)
- .y(50))
- .build()));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ ToolType::MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
break;
default:
FAIL() << "Source " << fromSource << " doesn't support drag and drop";
@@ -8349,8 +8359,11 @@
mDragWindow =
sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {mDragWindow, mSpyWindow, mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(),
+ *mWindow->getInfo(), *mSecondWindow->getInfo()},
+ {},
+ 0,
+ 0});
// Transfer touch focus to the drag window
bool transferred =
@@ -8369,7 +8382,7 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8378,7 +8391,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8387,7 +8400,7 @@
// Move back to original window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8395,7 +8408,8 @@
mSecondWindow->consumeDragEvent(true, -50, 50);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
mWindow->assertNoEvents();
@@ -8415,7 +8429,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8431,7 +8445,7 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8440,7 +8454,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8449,7 +8463,7 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
@@ -8463,7 +8477,7 @@
// Move on window and keep button pressed.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
@@ -8475,7 +8489,7 @@
// Move to another window and release button, expect to drop item.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -8488,7 +8502,7 @@
// nothing to the window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -8504,11 +8518,12 @@
// Set second window invisible.
mSecondWindow->setVisible(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8517,7 +8532,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8526,7 +8541,7 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
@@ -8540,7 +8555,7 @@
mWindow->setPreventSplitting(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8553,7 +8568,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(75).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionPointerDown(/*pointerIndex=*/1);
@@ -8565,7 +8580,7 @@
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
// First down on second window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8580,7 +8595,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8596,7 +8611,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
mWindow->consumeDragEvent(false, 50, 50);
@@ -8610,7 +8625,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
@@ -8624,11 +8639,16 @@
// Update window of second display.
sp<FakeWindowHandle> windowInSecondary =
sp<FakeWindowHandle>::make(mApp, mDispatcher, "D_2", SECOND_DISPLAY_ID);
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+ *mSecondWindow->getInfo(), *windowInSecondary->getInfo()},
+ {},
+ 0,
+ 0});
// Let second display has a touch state.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.displayId(SECOND_DISPLAY_ID)
@@ -8637,11 +8657,16 @@
windowInSecondary->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
SECOND_DISPLAY_ID, /*expectedFlag=*/0);
// Update window again.
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+ *mSecondWindow->getInfo(), *windowInSecondary->getInfo()},
+ {},
+ 0,
+ 0});
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8650,7 +8675,7 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8659,7 +8684,7 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
@@ -8672,11 +8697,10 @@
startDrag(true, AINPUT_SOURCE_MOUSE);
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(50)
.y(50))
.build()))
@@ -8687,11 +8711,10 @@
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(150)
.y(50))
.build()))
@@ -8702,11 +8725,10 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(150)
.y(50))
.build()))
@@ -8726,7 +8748,7 @@
window->setDropInput(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -8742,7 +8764,7 @@
// With the flag cleared, the window should get input
window->setDropInput(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -8769,7 +8791,8 @@
window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -8785,7 +8808,8 @@
// With the flag cleared, the window should get input
window->setDropInputIfObscured(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -8812,7 +8836,8 @@
window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -8827,7 +8852,8 @@
window->assertNoEvents();
// When the window is no longer obscured because it went on top, it should get input
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *obscuringWindow->getInfo()}, {}, 0, 0});
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
@@ -8863,8 +8889,11 @@
mThirdWindow->setFocusable(true);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}},
- {SECOND_DISPLAY_ID, {mThirdWindow}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*mWindow->getInfo(), *mSecondWindow->getInfo(), *mThirdWindow->getInfo()},
+ {},
+ 0,
+ 0});
mThirdWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
mWindow->consumeFocusEvent(true);
@@ -8945,13 +8974,14 @@
TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
// Interact with the window first.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
// Then remove focus.
mWindow->setFocusable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
// Assert that caller can switch touch mode by owning one of the last interacted window.
const WindowInfo& windowInfo = *mWindow->getInfo();
@@ -8993,11 +9023,12 @@
* Adding a spy window that is not a trusted overlay causes Dispatcher to abort.
*/
TEST_F(InputDispatcherSpyWindowDeathTest, UntrustedSpy_AbortsDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
ScopedSilentDeath _silentDeath;
auto spy = createSpy();
spy->setTrustedOverlay(false);
- ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}),
+ ASSERT_DEATH(mDispatcher->onWindowInfosChanged({{*spy->getInfo()}, {}, 0, 0}),
".* not a trusted overlay");
}
@@ -9006,10 +9037,10 @@
*/
TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
auto spy = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
@@ -9031,7 +9062,8 @@
auto spy1 = createSpy();
auto spy2 = createSpy();
auto spy3 = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window, spy3}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spy1->getInfo(), *spy2->getInfo(), *window->getInfo(), *spy3->getInfo()}, {}, 0, 0});
const std::vector<sp<FakeWindowHandle>> channels{spy1, spy2, window, spy3};
const size_t numChannels = channels.size();
@@ -9048,7 +9080,7 @@
}
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
std::vector<size_t> eventOrder;
@@ -9083,10 +9115,10 @@
auto window = createForeground();
auto spy = createSpy();
spy->setTouchable(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
spy->assertNoEvents();
@@ -9101,23 +9133,23 @@
auto window = createForeground();
auto spy = createSpy();
spy->setTouchableRegion(Region{{0, 0, 20, 20}});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// Inject an event outside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionUp();
spy->assertNoEvents();
// Inject an event inside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{5, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9135,11 +9167,11 @@
spy->setWatchOutsideTouch(true);
spy->setOwnerInfo(gui::Pid{56}, gui::Uid{78});
spy->setFrame(Rect{0, 0, 20, 20});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// Inject an event outside the spy window's frame and touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9157,10 +9189,11 @@
windowRight->setFrame({100, 0, 200, 200});
auto spy = createSpy();
spy->setFrame({0, 0, 200, 200});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowLeft->consumeMotionDown();
@@ -9173,7 +9206,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowRight->consumeMotionDown();
@@ -9189,10 +9222,10 @@
window->setFrame({0, 0, 200, 200});
auto spyRight = createSpy();
spyRight->setFrame({100, 0, 200, 200});
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}});
+ mDispatcher->onWindowInfosChanged({{*spyRight->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9205,7 +9238,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionPointerDown(/*pointerIndex=*/1);
@@ -9225,11 +9258,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 100, 100));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down, no window touched.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -9244,7 +9277,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -9261,15 +9294,15 @@
spy->setFocusable(false);
auto window = createForeground();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyUp(ADISPLAY_ID_NONE);
@@ -9286,10 +9319,11 @@
auto window = createForeground();
auto spy1 = createSpy();
auto spy2 = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window}}});
+ mDispatcher->onWindowInfosChanged(
+ {{*spy1->getInfo(), *spy2->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy1->consumeMotionDown();
@@ -9303,7 +9337,7 @@
// The rest of the gesture should only be sent to the second spy window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy2->consumeMotionMove();
@@ -9318,10 +9352,10 @@
TEST_F(InputDispatcherPilferPointersTest, CanPilferAfterWindowIsRemovedMidStream) {
auto window = createForeground();
auto spy = createSpy();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -9331,7 +9365,7 @@
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
@@ -9344,11 +9378,11 @@
auto spy = createSpy();
auto window = createForeground();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on the window and the spy.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
@@ -9367,7 +9401,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -9383,7 +9417,7 @@
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(-5).y(-5))
.build();
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
@@ -9400,11 +9434,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on the window only
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 150}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9418,7 +9452,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
@@ -9434,7 +9468,7 @@
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionPointerDown(1);
@@ -9442,8 +9476,8 @@
// Spy window pilfers the pointers.
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
- window->consumeMotionPointerUp(/* idx */ 2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
- window->consumeMotionPointerUp(/* idx */ 1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ window->consumeMotionPointerUp(/*idx=*/2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ window->consumeMotionPointerUp(/*idx=*/1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
spy->assertNoEvents();
window->assertNoEvents();
@@ -9460,11 +9494,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on both spy and window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9479,7 +9513,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionPointerDown(1);
@@ -9503,11 +9537,11 @@
auto window = createForeground();
window->setFrame(Rect(0, 0, 200, 200));
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
// First finger down on both window and spy
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9526,7 +9560,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9560,7 +9594,7 @@
window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
return {std::move(overlay), std::move(window)};
@@ -9590,13 +9624,14 @@
auto [overlay, window] = setupStylusOverlayScenario();
overlay->setTrustedOverlay(false);
// Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort.
- ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}),
+ ASSERT_DEATH(mDispatcher->onWindowInfosChanged(
+ {{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0}),
".* not a trusted overlay");
}
TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) {
auto [overlay, window] = setupStylusOverlayScenario();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
overlay->consumeMotionDown();
@@ -9615,7 +9650,7 @@
TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
auto [overlay, window] = setupStylusOverlayScenario();
overlay->setSpy(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
overlay->consumeMotionDown();
@@ -9644,7 +9679,7 @@
TEST_F(InputDispatcherStylusInterceptorTest, StylusHandwritingScenario) {
auto [overlay, window] = setupStylusOverlayScenario();
overlay->setSpy(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
overlay->consumeMotionDown();
@@ -9656,7 +9691,7 @@
// The interceptor configures itself so that it is no longer a spy.
overlay->setSpy(false);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
// It continues to receive the rest of the stylus gesture.
sendStylusEvent(AMOTION_EVENT_ACTION_MOVE);
@@ -9677,7 +9712,7 @@
: mPid(pid), mUid(uid), mDispatcher(dispatcher) {}
InputEventInjectionResult injectTargetedMotion(int32_t action) const {
- return injectMotionEvent(mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
+ return injectMotionEvent(*mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {100, 200},
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -9686,7 +9721,7 @@
}
InputEventInjectionResult injectTargetedKey(int32_t action) const {
- return inputdispatcher::injectKey(mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ return inputdispatcher::injectKey(*mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
InputEventInjectionSync::WAIT_FOR_RESULT,
INJECT_EVENT_TIMEOUT, /*allowKeyRepeat=*/false, {mUid},
mPolicyFlags);
@@ -9708,7 +9743,7 @@
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedWindow) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
@@ -9725,7 +9760,7 @@
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
@@ -9745,7 +9780,7 @@
auto spy = owner.createWindow();
spy->setSpy(true);
spy->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
@@ -9761,7 +9796,7 @@
auto randosSpy = rando.createWindow();
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+ mDispatcher->onWindowInfosChanged({{*randosSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
// The event is targeted at owner's window, so injection should succeed, but the spy should
// not receive the event.
@@ -9779,11 +9814,11 @@
auto randosSpy = rando.createWindow();
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+ mDispatcher->onWindowInfosChanged({{*randosSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
// A user that has injection permission can inject into any window.
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT));
randosSpy->consumeMotionDown();
window->consumeMotionDown();
@@ -9791,7 +9826,7 @@
setFocusedWindow(randosSpy);
randosSpy->consumeFocusEvent(true);
- EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher));
randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
window->assertNoEvents();
}
@@ -9804,7 +9839,7 @@
auto randosWindow = rando.createWindow();
randosWindow->setFrame(Rect{-10, -10, -5, -5});
randosWindow->setWatchOutsideTouch(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosWindow, window}}});
+ mDispatcher->onWindowInfosChanged({{*randosWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Do not allow generation of ACTION_OUTSIDE events into windows owned by different uids.
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d1c3f7d..477beaf 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -161,6 +161,7 @@
// fake mapping which would normally come from keyCharacterMap
std::unordered_map<int32_t, int32_t> mKeyCodeMapping;
std::vector<int32_t> mSupportedKeyCodes;
+ std::list<NotifyArgs> mProcessResult;
std::mutex mLock;
std::condition_variable mStateChangedCondition;
@@ -191,6 +192,14 @@
mMetaState = metaState;
}
+ // Sets the return value for the `process` call.
+ void setProcessResult(std::list<NotifyArgs> notifyArgs) {
+ mProcessResult.clear();
+ for (auto notifyArg : notifyArgs) {
+ mProcessResult.push_back(notifyArg);
+ }
+ }
+
void assertConfigureWasCalled() {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -291,7 +300,7 @@
mLastEvent = *rawEvent;
mProcessWasCalled = true;
mStateChangedCondition.notify_all();
- return {};
+ return mProcessResult;
}
int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
@@ -1877,7 +1886,7 @@
::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>;
TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes);
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsGenerateKeyEvents) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) {
const auto stylusId = TestFixture::mStylusInfo.getId();
TestFixture::mStylus->pressKey(BTN_STYLUS);
@@ -1891,7 +1900,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -1937,7 +1946,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsSurroundingHoveringTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -2013,7 +2022,7 @@
WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonsWithinTouchGesture) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) {
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
const auto stylusId = TestFixture::mStylusInfo.getId();
@@ -2067,7 +2076,7 @@
WithDeviceId(touchscreenId))));
}
-TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonMotionEventsDisabled) {
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
TestFixture::mReader->requestRefreshConfiguration(
InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
@@ -2124,7 +2133,7 @@
// ongoing stylus gesture that is being emitted by the touchscreen.
using ExternalStylusIntegrationTest = TouchIntegrationTest;
-TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureReported) {
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus capable of reporting pressure data that
@@ -2170,7 +2179,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
-TEST_F(ExternalStylusIntegrationTest, DISABLED_FusedExternalStylusPressureNotReported) {
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus capable of reporting pressure data that
@@ -2238,7 +2247,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
}
-TEST_F(ExternalStylusIntegrationTest, DISABLED_UnfusedExternalStylus) {
+TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
const Point centerPoint = mDevice->getCenterPoint();
// Create an external stylus device that does not support pressure. It should not affect any
@@ -2475,6 +2484,73 @@
ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
}
+TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1");
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ NotifyMotionArgs args1;
+ NotifySwitchArgs args2;
+ NotifyKeyArgs args3;
+ mapper.setProcessResult({args1, args2, args3});
+
+ InputReaderConfiguration config;
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+ RawEvent event;
+ event.deviceId = EVENTHUB_ID;
+ std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+ for (auto& arg : notifyArgs) {
+ if (const auto notifyMotionArgs = std::get_if<NotifyMotionArgs>(&arg)) {
+ ASSERT_EQ(POLICY_FLAG_WAKE, notifyMotionArgs->policyFlags);
+ } else if (const auto notifySwitchArgs = std::get_if<NotifySwitchArgs>(&arg)) {
+ ASSERT_EQ(POLICY_FLAG_WAKE, notifySwitchArgs->policyFlags);
+ } else if (const auto notifyKeyArgs = std::get_if<NotifyKeyArgs>(&arg)) {
+ ASSERT_EQ(POLICY_FLAG_WAKE, notifyKeyArgs->policyFlags);
+ }
+ }
+}
+
+TEST_F(InputDeviceTest, NotWakeDevice_DoesNotAddWakeFlagToProcessNotifyArgs) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0");
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ NotifyMotionArgs args;
+ mapper.setProcessResult({args});
+
+ InputReaderConfiguration config;
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+ RawEvent event;
+ event.deviceId = EVENTHUB_ID;
+ std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+ // POLICY_FLAG_WAKE is not added to the NotifyArgs.
+ ASSERT_EQ(0u, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags);
+}
+
+TEST_F(InputDeviceTest, NotWakeDevice_DoesNotRemoveExistingWakeFlagFromProcessNotifyArgs) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "0");
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ NotifyMotionArgs args;
+ args.policyFlags = POLICY_FLAG_WAKE;
+ mapper.setProcessResult({args});
+
+ InputReaderConfiguration config;
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
+
+ RawEvent event;
+ event.deviceId = EVENTHUB_ID;
+ std::list<NotifyArgs> notifyArgs = mDevice->process(&event, 1);
+
+ // The POLICY_FLAG_WAKE is preserved, despite the device being a non-wake device.
+ ASSERT_EQ(POLICY_FLAG_WAKE, std::get<NotifyMotionArgs>(notifyArgs.front()).policyFlags);
+}
+
// A single input device is associated with a specific display. Check that:
// 1. Device is disabled if the viewport corresponding to the associated display is not found
// 2. Device is disabled when setEnabled API is called
@@ -3727,6 +3803,19 @@
ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
}
+TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ NotifyKeyArgs args;
+
+ // Key down
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
+}
+
// --- KeyboardInputMapperTest_ExternalDevice ---
class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
@@ -9373,6 +9462,11 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
}
+/**
+ * When the viewport is deactivated (isActive transitions from true to false),
+ * and touch.enableForInactiveViewport is false, touches prior to the transition
+ * should be cancelled.
+ */
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "0");
@@ -9424,6 +9518,60 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
}
+/**
+ * When the viewport is deactivated (isActive transitions from true to false),
+ * and touch.enableForInactiveViewport is true, touches prior to the transition
+ * should not be cancelled.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_TouchesNotAborted) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.enableForInactiveViewport", "1");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ std::optional<DisplayViewport> optionalDisplayViewport =
+ mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
+ ASSERT_TRUE(optionalDisplayViewport.has_value());
+ DisplayViewport displayViewport = *optionalDisplayViewport;
+
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
+
+ // Finger down
+ int32_t x = 100, y = 100;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Deactivate display viewport
+ displayViewport.isActive = false;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // The ongoing touch should not be canceled
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Finger move is not ignored
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Reactivate display viewport
+ displayViewport.isActive = true;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Finger move continues and does not start new gesture
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+}
+
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
// Setup the first touch screen device.
prepareAxes(POSITION | ID | SLOT);
diff --git a/services/inputflinger/tests/SlopController_test.cpp b/services/inputflinger/tests/SlopController_test.cpp
new file mode 100644
index 0000000..f524acd
--- /dev/null
+++ b/services/inputflinger/tests/SlopController_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../reader/mapper/SlopController.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// --- SlopControllerTest ---
+
+TEST(SlopControllerTest, PositiveValues) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller.consumeEvent(1000, 1));
+ ASSERT_EQ(0, controller.consumeEvent(1003, 3));
+ ASSERT_EQ(2, controller.consumeEvent(1005, 3));
+ ASSERT_EQ(4, controller.consumeEvent(1009, 4));
+
+ SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller2.consumeEvent(1000, 5));
+ ASSERT_EQ(3, controller2.consumeEvent(1003, 3));
+ ASSERT_EQ(4, controller2.consumeEvent(1005, 4));
+}
+
+TEST(SlopControllerTest, NegativeValues) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller.consumeEvent(1000, -1));
+ ASSERT_EQ(0, controller.consumeEvent(1003, -3));
+ ASSERT_EQ(-2, controller.consumeEvent(1005, -3));
+ ASSERT_EQ(-4, controller.consumeEvent(1009, -4));
+
+ SlopController controller2 = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller2.consumeEvent(1000, -5));
+ ASSERT_EQ(-3, controller2.consumeEvent(1003, -3));
+ ASSERT_EQ(-4, controller2.consumeEvent(1005, -4));
+}
+
+TEST(SlopControllerTest, ZeroDoesNotResetSlop) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+ ASSERT_EQ(0, controller.consumeEvent(1006, 0));
+ ASSERT_EQ(2, controller.consumeEvent(1008, 2));
+}
+
+TEST(SlopControllerTest, SignChange_ResetsSlop) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(0, controller.consumeEvent(1000, 2));
+ ASSERT_EQ(0, controller.consumeEvent(1001, -4));
+ ASSERT_EQ(0, controller.consumeEvent(1002, 3));
+ ASSERT_EQ(0, controller.consumeEvent(1003, -2));
+
+ ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+ ASSERT_EQ(0, controller.consumeEvent(1006, 0));
+ ASSERT_EQ(2, controller.consumeEvent(1008, 2));
+
+ ASSERT_EQ(0, controller.consumeEvent(1010, -4));
+ ASSERT_EQ(-1, controller.consumeEvent(1011, -2));
+
+ ASSERT_EQ(0, controller.consumeEvent(1015, 5));
+ ASSERT_EQ(2, controller.consumeEvent(1016, 2));
+
+ ASSERT_EQ(0, controller.consumeEvent(1017, -5));
+ ASSERT_EQ(-2, controller.consumeEvent(1018, -2));
+}
+
+TEST(SlopControllerTest, OldAge_ResetsSlop) {
+ SlopController controller = SlopController(/*slopThreshold=*/5, /*slopDurationNanos=*/100);
+
+ ASSERT_EQ(1, controller.consumeEvent(1005, 6));
+ ASSERT_EQ(0, controller.consumeEvent(1108, 2)); // age exceeds slop duration
+
+ ASSERT_EQ(1, controller.consumeEvent(1110, 4));
+ ASSERT_EQ(0, controller.consumeEvent(1210, 2)); // age equals slop duration
+
+ ASSERT_EQ(0, controller.consumeEvent(1215, -3));
+ ASSERT_EQ(-2, controller.consumeEvent(1216, -4));
+ ASSERT_EQ(-5, controller.consumeEvent(1315, -5));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.cpp b/services/inputflinger/tests/TestInputListenerMatchers.cpp
new file mode 100644
index 0000000..1464e60
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListenerMatchers.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestInputListenerMatchers.h"
+
+namespace android {
+
+WithKeyActionMatcher WithKeyAction(int32_t action) {
+ return WithKeyActionMatcher(action);
+}
+
+WithMotionActionMatcher WithMotionAction(int32_t action) {
+ return WithMotionActionMatcher(action);
+}
+
+WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
+ return WithDisplayIdMatcher(displayId);
+}
+
+WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+ return WithDeviceIdMatcher(deviceId);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 70bad7c..020ea86 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -23,53 +23,145 @@
#include <gtest/gtest.h>
#include <input/Input.h>
+#include "NotifyArgs.h"
#include "TestConstants.h"
namespace android {
-MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
- bool matches = action == arg.action;
- if (!matches) {
- *result_listener << "expected action " << MotionEvent::actionToString(action)
- << ", but got " << MotionEvent::actionToString(arg.action);
- }
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
-}
-
-MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
- *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got "
- << KeyEvent::actionToString(arg.action);
- return arg.action == action;
-}
-
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.source);
return arg.source == source;
}
-MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
- *result_listener << "expected displayId " << displayId << ", but got " << arg.displayId;
- return arg.displayId == displayId;
-}
+/// Key action
+class WithKeyActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
-MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
- *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId;
- return arg.deviceId == deviceId;
-}
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mAction == args.action;
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mAction == event.getAction();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with key action " << KeyEvent::actionToString(mAction);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+WithKeyActionMatcher WithKeyAction(int32_t action);
+
+/// Motion action
+class WithMotionActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ bool matches = mAction == args.action;
+ if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ bool matches = mAction == event.getAction();
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with motion action " << MotionEvent::actionToString(mAction);
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ *os << " and FLAG_CANCELED";
+ }
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+WithMotionActionMatcher WithMotionAction(int32_t action);
+
+/// Display Id
+class WithDisplayIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDisplayId == event.getDisplayId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
+
+private:
+ const int32_t mDisplayId;
+};
+
+WithDisplayIdMatcher WithDisplayId(int32_t displayId);
+
+/// Device Id
+class WithDeviceIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDeviceId == event.getDeviceId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+ const int32_t mDeviceId;
+};
+
+WithDeviceIdMatcher WithDeviceId(int32_t deviceId);
MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
*result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
return arg.keyCode == keyCode;
}
+MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
+ return arg.getRepeatCount() == repeatCount;
+}
+
MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
*result_listener << "expected " << count << " pointer(s), but got " << arg.getPointerCount();
return arg.getPointerCount() == count;
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index da0815f..7cfcf71 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -88,14 +88,13 @@
}
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, eventTime, /*readTime=*/0, DEVICE_ID,
- AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, POLICY_FLAG_PASS_TO_USER,
- action, /* actionButton */ 0,
- /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+ NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ /*displayId=*/0, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0,
+ /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+ pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {});
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /*videoFrames=*/{});
return args;
}
@@ -104,15 +103,15 @@
InputDeviceIdentifier identifier;
auto info = InputDeviceInfo();
- info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias",
- /*isExternal*/ false, /*hasMic*/ false, ADISPLAY_ID_NONE);
+ info.initialize(DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
+ /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
info.addSource(AINPUT_SOURCE_TOUCHSCREEN);
- info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0,
- /*fuzz*/ 0, X_RESOLUTION);
- info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0,
- /*fuzz*/ 0, Y_RESOLUTION);
+ info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat=*/0,
+ /*fuzz=*/0, X_RESOLUTION);
+ info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat=*/0,
+ /*fuzz=*/0, Y_RESOLUTION);
info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255,
- /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION);
+ /*flat=*/0, /*fuzz=*/0, MAJOR_RESOLUTION);
return info;
}
@@ -152,7 +151,7 @@
}
TEST(RemovePointerIdsTest, RemoveOnePointer) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
NotifyMotionArgs pointer1Only = removePointerIds(args, {0});
@@ -167,7 +166,7 @@
*/
TEST(RemovePointerIdsTest, RemoveTwoPointers) {
NotifyMotionArgs args =
- generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE,
+ generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, AMOTION_EVENT_ACTION_MOVE,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2});
@@ -179,7 +178,7 @@
* pointer during a POINTER_DOWN event.
*/
TEST(RemovePointerIdsTest, ActionPointerDown) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
NotifyMotionArgs pointers0And2 = removePointerIds(args, {1});
@@ -193,7 +192,7 @@
* Remove all pointers during a MOVE event.
*/
TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0,
AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
NotifyMotionArgs noPointers = removePointerIds(args, {0, 1});
@@ -205,7 +204,7 @@
* then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event.
*/
TEST(RemovePointerIdsTest, PointerDownBecomesDown) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2});
@@ -220,11 +219,11 @@
* If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event.
*/
TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_TRUE(result.empty());
}
@@ -232,11 +231,11 @@
* If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped
*/
TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_TRUE(result.empty());
}
@@ -244,11 +243,11 @@
* If a pointer is already suppressed, it should be removed from a MOVE event.
*/
TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
}
@@ -259,11 +258,11 @@
* 2) A MOVE event without this pointer
*/
TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_EQ(2u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -275,10 +274,10 @@
* should be canceled with ACTION_CANCEL.
*/
TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}});
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {0});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -289,11 +288,11 @@
* but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional.
*/
TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -304,11 +303,11 @@
* errors with handling pointer index inside the action.
*/
TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_0_UP,
{{1, 2, 3}, {4, 5, 6}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {0});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -320,10 +319,10 @@
*/
TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) {
NotifyMotionArgs args =
- generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}});
+ generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, MOVE, {{1, 2, 3}, {4, 5, 6}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {},
- /*newSuppressedPointerIds*/ {0, 1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{},
+ /*newSuppressedPointerIds=*/{0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -335,11 +334,11 @@
* would undo the entire gesture.
*/
TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_1_UP,
{{1, 2, 3}, {4, 5, 6}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1},
- /*newSuppressedPointerIds*/ {0, 1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{1},
+ /*newSuppressedPointerIds=*/{0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
ASSERT_EQ(FLAG_CANCELED, result[0].flags);
@@ -350,11 +349,11 @@
* this should become a regular DOWN event because it's the only pointer that will be valid now.
*/
TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN,
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, POINTER_2_DOWN,
{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
std::vector<NotifyMotionArgs> result =
- cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1},
- /*newSuppressedPointerIds*/ {0, 1});
+ cancelSuppressedPointers(args, /*oldSuppressedPointerIds=*/{0, 1},
+ /*newSuppressedPointerIds=*/{0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], DOWN, {{2, {7, 8, 9}}});
ASSERT_EQ(0, result[0].flags);
@@ -365,7 +364,7 @@
* struct is populated as expected.
*/
TEST(GetTouchesTest, ConvertDownEvent) {
- NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}});
+ NotifyMotionArgs args = generateMotionArgs(/*downTime=*/0, /*eventTime=*/0, DOWN, {{1, 2, 3}});
AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo();
SlotState slotState;
SlotState oldSlotState = slotState;
@@ -1024,7 +1023,7 @@
mPalmRejector->processMotion(
generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
// Suppress both pointers!!
suppressPointerAtPosition(1414, 702);
@@ -1058,13 +1057,13 @@
mPalmRejector->processMotion(
generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
// Suppress second pointer (pointer 1)
suppressPointerAtPosition(1060, 700);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, MOVE,
+ generateMotionArgs(downTime, /*eventTime=*/1, MOVE,
{{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
ASSERT_EQ(2u, argsList.size());
ASSERT_EQ(POINTER_1_UP, argsList[0].action);
@@ -1076,20 +1075,20 @@
// A new pointer goes down and gets suppressed right away. It should just be dropped
suppressPointerAtPosition(1001, 601);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
ASSERT_EQ(0u, argsList.size());
// Likewise, pointer that's already canceled should be ignored
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_2_UP,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}}));
ASSERT_EQ(0u, argsList.size());
// Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier.
suppressPointerAtPosition(1417, 685);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP,
+ generateMotionArgs(downTime, /*eventTime=*/1, POINTER_1_UP,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(CANCEL, argsList[0].action);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 47b0824..d7980f5 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -100,6 +100,22 @@
}
cc_fuzz {
+ name: "inputflinger_touchpad_input_fuzzer",
+ defaults: [
+ "inputflinger_fuzz_defaults",
+ ],
+ srcs: [
+ "TouchpadInputFuzzer.cpp",
+ ],
+ static_libs: [
+ "libchrome-gestures",
+ ],
+ header_libs: [
+ "libchrome-gestures_headers",
+ ],
+}
+
+cc_fuzz {
name: "inputflinger_input_reader_fuzzer",
defaults: [
"inputflinger_fuzz_defaults",
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 8098ef2..af20a27 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -15,36 +15,47 @@
*/
#include <CursorInputMapper.h>
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
namespace android {
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
+static void addProperty(FuzzEventHub& eventHub, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty("cursor.mode", "pointer"); },
- [&]() -> void { fuzzer.addProperty("cursor.mode", "navigation"); },
+ {[&]() -> void { eventHub.addProperty("cursor.mode", "pointer"); },
+ [&]() -> void { eventHub.addProperty("cursor.mode", "navigation"); },
[&]() -> void {
- fuzzer.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data());
},
[&]() -> void {
- fuzzer.addProperty("cursor.orientationAware",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("cursor.orientationAware",
+ fdp->ConsumeRandomLengthString(100).data());
}})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>(policyConfig);
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ InputReaderConfiguration policyConfig;
+ CursorInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider, CursorInputMapper>(*fdp.get(), device,
+ policyConfig);
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
+ [&]() -> void {
+ addProperty(*eventHub.get(), fdp);
+ configureAndResetDevice(*fdp, device);
+ },
[&]() -> void {
std::string dump;
mapper.dump(dump);
@@ -65,22 +76,11 @@
mapper.populateDeviceInfo(info);
},
[&]() -> void {
- int32_t type, code;
- type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
-
// Need to reconfigure with 0 or you risk a NPE.
std::list<NotifyArgs> unused =
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
InputReaderConfiguration::Change(0));
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
unused += mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
deleted file mode 100644
index b992928..0000000
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <InputDevice.h>
-#include <InputMapper.h>
-#include <InputReader.h>
-#include <MapperHelpers.h>
-
-namespace android {
-
-class FuzzContainer {
- std::shared_ptr<FuzzEventHub> mFuzzEventHub;
- sp<FuzzInputReaderPolicy> mFuzzPolicy;
- FuzzInputListener mFuzzListener;
- std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
- std::unique_ptr<InputDevice> mFuzzDevice;
- InputReaderConfiguration mPolicyConfig;
- std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
-
-public:
- FuzzContainer(std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) : mFdp(fdp) {
- // Setup parameters.
- std::string deviceName = mFdp->ConsumeRandomLengthString(16);
- std::string deviceLocation = mFdp->ConsumeRandomLengthString(12);
- int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5);
- int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5);
-
- // Create mocked objects.
- mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
- mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
- mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
- mFuzzListener, mFdp);
-
- InputDeviceIdentifier identifier;
- identifier.name = deviceName;
- identifier.location = deviceLocation;
- mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
- identifier);
- mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
- }
-
- ~FuzzContainer() {}
-
- void configureDevice() {
- nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
- std::list<NotifyArgs> out;
- out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, /*changes=*/{});
- out += mFuzzDevice->reset(arbitraryTime);
- for (const NotifyArgs& args : out) {
- mFuzzListener.notify(args);
- }
- }
-
- void addProperty(std::string key, std::string value) {
- mFuzzEventHub->addProperty(key, value);
- configureDevice();
- }
-
- InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
-
- template <class T, typename... Args>
- T& getMapper(Args... args) {
- int32_t eventhubId = mFdp->ConsumeIntegral<int32_t>();
- // ensure a device entry exists for this eventHubId
- mFuzzDevice->addEmptyEventHubDevice(eventhubId);
- configureDevice();
-
- return mFuzzDevice->template constructAndAddMapper<T>(eventhubId, args...);
- }
-};
-
-} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 616e870..922cbdf 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -14,45 +14,52 @@
* limitations under the License.
*/
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
#include <KeyboardInputMapper.h>
+#include <MapperHelpers.h>
namespace android {
const int32_t kMaxKeycodes = 100;
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
+static void addProperty(FuzzEventHub& eventHub, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty("keyboard.orientationAware", "1"); },
+ {[&]() -> void { eventHub.addProperty("keyboard.orientationAware", "1"); },
[&]() -> void {
- fuzzer.addProperty("keyboard.orientationAware",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("keyboard.orientationAware",
+ fdp->ConsumeRandomLengthString(100).data());
},
[&]() -> void {
- fuzzer.addProperty("keyboard.doNotWakeByDefault",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("keyboard.doNotWakeByDefault",
+ fdp->ConsumeRandomLengthString(100).data());
},
[&]() -> void {
- fuzzer.addProperty("keyboard.handlesKeyRepeat",
- fdp->ConsumeRandomLengthString(100).data());
+ eventHub.addProperty("keyboard.handlesKeyRepeat",
+ fdp->ConsumeRandomLengthString(100).data());
}})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- KeyboardInputMapper& mapper =
- fuzzer.getMapper<KeyboardInputMapper>(policyConfig, fdp->ConsumeIntegral<uint32_t>(),
- fdp->ConsumeIntegral<int32_t>());
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ KeyboardInputMapper& mapper = getMapperForDevice<
+ ThreadSafeFuzzedDataProvider,
+ KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{},
+ /*source=*/fdp->ConsumeIntegral<uint32_t>(),
+ /*keyboardType=*/fdp->ConsumeIntegral<int32_t>());
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
+ [&]() -> void { addProperty(*eventHub.get(), fdp); },
[&]() -> void {
std::string dump;
mapper.dump(dump);
@@ -64,7 +71,7 @@
[&]() -> void { mapper.getSources(); },
[&]() -> void {
std::list<NotifyArgs> unused =
- mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), /*readerConfig=*/{},
InputReaderConfiguration::Change(
fdp->ConsumeIntegral<uint32_t>()));
},
@@ -72,17 +79,7 @@
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
},
[&]() -> void {
- int32_t type, code;
- type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 1ecaa64..bdedfdf 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -15,6 +15,10 @@
*/
#pragma once
+#include <map>
+#include <memory>
+
+#include <EventHub.h>
#include <InputDevice.h>
#include <InputMapper.h>
#include <InputReader.h>
@@ -22,7 +26,6 @@
constexpr size_t kValidTypes[] = {EV_SW,
EV_SYN,
- SYN_REPORT,
EV_ABS,
EV_KEY,
EV_MSC,
@@ -46,7 +49,6 @@
ABS_MT_PRESSURE,
ABS_MT_DISTANCE,
ABS_MT_TOOL_TYPE,
- SYN_MT_REPORT,
MSC_SCAN,
REL_X,
REL_Y,
@@ -74,10 +76,27 @@
return static_cast<ToolType>(toolType);
}
+template <class Fdp>
+RawEvent getFuzzedRawEvent(Fdp& fdp) {
+ const int32_t type = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidTypes)
+ : fdp.template ConsumeIntegral<int32_t>();
+ const int32_t code = fdp.ConsumeBool() ? fdp.PickValueInArray(kValidCodes)
+ : fdp.template ConsumeIntegral<int32_t>();
+ return RawEvent{
+ .when = fdp.template ConsumeIntegral<nsecs_t>(),
+ .readTime = fdp.template ConsumeIntegral<nsecs_t>(),
+ .deviceId = fdp.template ConsumeIntegral<int32_t>(),
+ .type = type,
+ .code = code,
+ .value = fdp.template ConsumeIntegral<int32_t>(),
+ };
+}
+
class FuzzEventHub : public EventHubInterface {
InputDeviceIdentifier mIdentifier;
std::vector<TouchVideoFrame> mVideoFrames;
PropertyMap mFuzzConfig;
+ std::map<int32_t /* deviceId */, std::map<int /* axis */, RawAbsoluteAxisInfo>> mAxes;
std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
public:
@@ -97,8 +116,18 @@
std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override {
return mFuzzConfig;
}
+ void setAbsoluteAxisInfo(int32_t deviceId, int axis, const RawAbsoluteAxisInfo& axisInfo) {
+ mAxes[deviceId][axis] = axisInfo;
+ }
status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const override {
+ if (auto deviceAxesIt = mAxes.find(deviceId); deviceAxesIt != mAxes.end()) {
+ const std::map<int, RawAbsoluteAxisInfo>& deviceAxes = deviceAxesIt->second;
+ if (auto axisInfoIt = deviceAxes.find(axis); axisInfoIt != deviceAxes.end()) {
+ *outAxisInfo = axisInfoIt->second;
+ return OK;
+ }
+ }
return mFdp->ConsumeIntegral<status_t>();
}
bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
@@ -118,18 +147,7 @@
std::vector<RawEvent> events;
const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
for (size_t i = 0; i < count; ++i) {
- int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes)
- : mFdp->ConsumeIntegral<int32_t>();
- int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes)
- : mFdp->ConsumeIntegral<int32_t>();
- events.push_back({
- .when = mFdp->ConsumeIntegral<nsecs_t>(),
- .readTime = mFdp->ConsumeIntegral<nsecs_t>(),
- .deviceId = mFdp->ConsumeIntegral<int32_t>(),
- .type = type,
- .code = code,
- .value = mFdp->ConsumeIntegral<int32_t>(),
- });
+ events.push_back(getFuzzedRawEvent(*mFdp));
}
return events;
}
@@ -277,7 +295,8 @@
}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) override {
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> layoutInfo) override {
return nullptr;
}
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
@@ -312,10 +331,8 @@
public:
FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- InputListenerInterface& listener,
- std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp)
- : mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {}
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp)
+ : mEventHub(eventHub), mPolicy(sp<FuzzInputReaderPolicy>::make(fdp)), mFdp(fdp) {}
~FuzzInputReaderContext() {}
void updateGlobalMetaState() override {}
int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
@@ -345,4 +362,32 @@
bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
};
+template <class Fdp>
+InputDevice getFuzzedInputDevice(Fdp& fdp, FuzzInputReaderContext* context) {
+ InputDeviceIdentifier identifier;
+ identifier.name = fdp.ConsumeRandomLengthString(16);
+ identifier.location = fdp.ConsumeRandomLengthString(12);
+ int32_t deviceID = fdp.ConsumeIntegralInRange(0, 5);
+ int32_t deviceGeneration = fdp.ConsumeIntegralInRange(0, 5);
+ return InputDevice(context, deviceID, deviceGeneration, identifier);
+}
+
+template <class Fdp>
+void configureAndResetDevice(Fdp& fdp, InputDevice& device) {
+ nsecs_t arbitraryTime = fdp.template ConsumeIntegral<nsecs_t>();
+ std::list<NotifyArgs> out;
+ out += device.configure(arbitraryTime, /*readerConfig=*/{}, /*changes=*/{});
+ out += device.reset(arbitraryTime);
+}
+
+template <class Fdp, class T, typename... Args>
+T& getMapperForDevice(Fdp& fdp, InputDevice& device, Args... args) {
+ int32_t eventhubId = fdp.template ConsumeIntegral<int32_t>();
+ // ensure a device entry exists for this eventHubId
+ device.addEmptyEventHubDevice(eventhubId);
+ configureAndResetDevice(fdp, device);
+
+ return device.template constructAndAddMapper<T>(eventhubId, args...);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 212462d..d3f6690 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -14,60 +14,72 @@
* limitations under the License.
*/
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
#include <MultiTouchInputMapper.h>
namespace android {
const int32_t kMaxKeycodes = 100;
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
+static void addProperty(FuzzEventHub& eventHub, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
- {[&]() -> void { fuzzer.addProperty("touch.deviceType", "touchScreen"); },
+ {[&]() -> void { eventHub.addProperty("touch.deviceType", "touchScreen"); },
[&]() -> void {
- fuzzer.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.isSummed",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.isSummed",
+ fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.calibration",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.size.calibration",
+ fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.pressure.scale",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.pressure.scale",
+ fdp->ConsumeRandomLengthString(8).data());
},
[&]() -> void {
- fuzzer.addProperty("touch.size.calibration",
- fdp->ConsumeBool() ? "diameter" : "area");
+ eventHub.addProperty("touch.size.calibration",
+ fdp->ConsumeBool() ? "diameter" : "area");
},
[&]() -> void {
- fuzzer.addProperty("touch.pressure.calibration",
- fdp->ConsumeRandomLengthString(8).data());
+ eventHub.addProperty("touch.pressure.calibration",
+ fdp->ConsumeRandomLengthString(8).data());
}})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>(policyConfig);
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ InputReaderConfiguration policyConfig;
+ MultiTouchInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider, MultiTouchInputMapper>(*fdp.get(),
+ device,
+ policyConfig);
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
fdp->PickValueInArray<std::function<void()>>({
- [&]() -> void { addProperty(fuzzer, fdp); },
+ [&]() -> void {
+ addProperty(*eventHub.get(), fdp);
+ configureAndResetDevice(*fdp, device);
+ },
[&]() -> void {
std::string dump;
mapper.dump(dump);
@@ -87,16 +99,7 @@
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
},
[&]() -> void {
- int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index 590207e..ac2030a 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include <FuzzContainer.h>
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
#include <SwitchInputMapper.h>
namespace android {
@@ -22,10 +24,15 @@
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
- FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(policyConfig);
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ SwitchInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider,
+ SwitchInputMapper>(*fdp.get(), device, InputReaderConfiguration{});
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
@@ -36,16 +43,7 @@
},
[&]() -> void { mapper.getSources(); },
[&]() -> void {
- int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
- : fdp->ConsumeIntegral<int32_t>();
- int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
- : fdp->ConsumeIntegral<int32_t>();
- RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<int32_t>(),
- type,
- code,
- fdp->ConsumeIntegral<int32_t>()};
+ RawEvent rawEvent = getFuzzedRawEvent(*fdp);
std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
new file mode 100644
index 0000000..be765cc
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <linux/input-event-codes.h>
+
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
+#include <TouchpadInputMapper.h>
+
+namespace android {
+
+namespace {
+
+void setAxisInfo(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub, int32_t id, int axis) {
+ if (fdp.ConsumeBool()) {
+ eventHub.setAbsoluteAxisInfo(id, axis,
+ RawAbsoluteAxisInfo{
+ .valid = fdp.ConsumeBool(),
+ .minValue = fdp.ConsumeIntegral<int32_t>(),
+ .maxValue = fdp.ConsumeIntegral<int32_t>(),
+ .flat = fdp.ConsumeIntegral<int32_t>(),
+ .fuzz = fdp.ConsumeIntegral<int32_t>(),
+ .resolution = fdp.ConsumeIntegral<int32_t>(),
+ });
+ }
+}
+
+void setAxisInfos(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub, int32_t id) {
+ setAxisInfo(fdp, eventHub, id, ABS_MT_SLOT);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_POSITION_X);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_POSITION_Y);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_PRESSURE);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_ORIENTATION);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_TOUCH_MAJOR);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_TOUCH_MINOR);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_WIDTH_MAJOR);
+ setAxisInfo(fdp, eventHub, id, ABS_MT_WIDTH_MINOR);
+}
+
+const std::vector<std::string> boolPropertiesToFuzz = {
+ "gestureProp.Compute_Surface_Area_from_Pressure",
+ "gestureProp.Drumroll_Suppression_Enable",
+ "gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls",
+ "gestureProp.Stationary_Wiggle_Filter_Enabled",
+};
+const std::vector<std::string> doublePropertiesToFuzz = {
+ "gestureProp.Fake_Timestamp_Delta",
+ "gestureProp.Finger_Moving_Energy",
+ "gestureProp.Finger_Moving_Hysteresis",
+ "gestureProp.IIR_a1",
+ "gestureProp.IIR_a2",
+ "gestureProp.IIR_b0",
+ "gestureProp.IIR_b1",
+ "gestureProp.IIR_b2",
+ "gestureProp.IIR_b3",
+ "gestureProp.Max_Allowed_Pressure_Change_Per_Sec",
+ "gestureProp.Max_Hysteresis_Pressure_Per_Sec",
+ "gestureProp.Max_Stationary_Move_Speed",
+ "gestureProp.Max_Stationary_Move_Speed_Hysteresis",
+ "gestureProp.Max_Stationary_Move_Suppress_Distance",
+ "gestureProp.Multiple_Palm_Width",
+ "gestureProp.Palm_Edge_Zone_Width",
+ "gestureProp.Palm_Eval_Timeout",
+ "gestureProp.Palm_Pressure",
+ "gestureProp.Palm_Width",
+ "gestureProp.Pressure_Calibration_Offset",
+ "gestureProp.Pressure_Calibration_Slope",
+ "gestureProp.Tap_Exclusion_Border_Width",
+ "gestureProp.Touchpad_Device_Output_Bias_on_X-Axis",
+ "gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis",
+ "gestureProp.Two_Finger_Vertical_Close_Distance_Thresh",
+};
+
+void setDeviceSpecificConfig(ThreadSafeFuzzedDataProvider& fdp, FuzzEventHub& eventHub) {
+ // There are a great many gesture properties offered by the Gestures library, all of which could
+ // potentially be set in Input Device Configuration files. Maintaining a complete list is
+ // impractical, so instead we only fuzz properties which are used in at least one IDC file, or
+ // which are likely to be used in future (e.g. ones for controlling palm rejection).
+
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty("gestureProp.Touchpad_Stack_Version",
+ std::to_string(fdp.ConsumeIntegral<int>()));
+ }
+
+ for (auto& propertyName : boolPropertiesToFuzz) {
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty(propertyName, fdp.ConsumeBool() ? "1" : "0");
+ }
+ }
+
+ for (auto& propertyName : doublePropertiesToFuzz) {
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty(propertyName, std::to_string(fdp.ConsumeFloatingPoint<double>()));
+ }
+ }
+
+ if (fdp.ConsumeBool()) {
+ eventHub.addProperty("gestureProp." + fdp.ConsumeRandomLengthString(),
+ std::to_string(fdp.ConsumeIntegral<int>()));
+ }
+}
+
+void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfiguration& config) {
+ config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7);
+ config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool();
+ config.touchpadTapToClickEnabled = fdp.ConsumeBool();
+ config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
+}
+
+} // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
+
+ // Create mocked objects to support the fuzzed input mapper.
+ std::shared_ptr<FuzzEventHub> eventHub = std::make_shared<FuzzEventHub>(fdp);
+ FuzzInputReaderContext context(eventHub, fdp);
+ InputDevice device = getFuzzedInputDevice(*fdp, &context);
+
+ setAxisInfos(*fdp, *eventHub.get(), device.getId());
+ setDeviceSpecificConfig(*fdp, *eventHub.get());
+
+ InputReaderConfiguration policyConfig;
+ // Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the
+ // TouchpadInputMapper constructor.
+ setTouchpadSettings(*fdp, policyConfig);
+ policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
+ TouchpadInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider, TouchpadInputMapper>(*fdp, device,
+ policyConfig);
+
+ // Loop through mapper operations until randomness is exhausted.
+ while (fdp->remaining_bytes() > 0) {
+ fdp->PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ std::string dump;
+ mapper.dump(dump);
+ },
+ [&]() -> void {
+ InputDeviceInfo info;
+ mapper.populateDeviceInfo(info);
+ },
+ [&]() -> void { mapper.getSources(); },
+ [&]() -> void {
+ setTouchpadSettings(*fdp, policyConfig);
+ policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
+ std::list<NotifyArgs> unused =
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ InputReaderConfiguration::Change(
+ fdp->ConsumeIntegral<uint32_t>()));
+ },
+ [&]() -> void {
+ std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+ },
+ [&]() -> void {
+ RawEvent event = getFuzzedRawEvent(*fdp);
+ std::list<NotifyArgs> unused = mapper.process(&event);
+ },
+ })();
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index b34e54f..2523f3b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -33,6 +33,7 @@
shared_libs: [
"libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
@@ -40,7 +41,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
export_shared_lib_headers: [
@@ -48,7 +49,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
cflags: [
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index f89035f..9a23c84 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -15,11 +15,11 @@
*/
#define LOG_TAG "PowerHalController"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
#include <powermanager/PowerHalController.h>
#include <powermanager/PowerHalLoader.h>
#include <utils/Log.h>
@@ -33,7 +33,8 @@
// -------------------------------------------------------------------------------------------------
std::unique_ptr<HalWrapper> HalConnector::connect() {
- if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) {
+ if (std::shared_ptr<aidl::android::hardware::power::IPower> halAidl =
+ PowerHalLoader::loadAidl()) {
return std::make_unique<AidlHalWrapper>(halAidl);
}
// If V1_0 isn't defined, none of them are
@@ -90,20 +91,24 @@
return result;
}
-HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost,
+ int32_t durationMs) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->setBoost(boost, durationMs);
return processHalResult(result, "setBoost");
}
-HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) {
+HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode,
+ bool enabled) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->setMode(mode, enabled);
return processHalResult(result, "setMode");
}
-HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession(
- int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
+PowerHalController::createHintSession(int32_t tgid, int32_t uid,
+ const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) {
std::shared_ptr<HalWrapper> handle = initHal();
auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
return processHalResult(result, "createHintSession");
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 6bd40f8..2214461 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -16,10 +16,11 @@
#define LOG_TAG "PowerHalLoader"
+#include <aidl/android/hardware/power/IPower.h>
+#include <android/binder_manager.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/IPower.h>
#include <binder/IServiceManager.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
@@ -54,7 +55,7 @@
// -------------------------------------------------------------------------------------------------
std::mutex PowerHalLoader::gHalMutex;
-sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::gHalAidl = nullptr;
sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
@@ -69,11 +70,30 @@
gHalHidlV1_3 = nullptr;
}
-sp<IPower> PowerHalLoader::loadAidl() {
+std::shared_ptr<aidl::android::hardware::power::IPower> PowerHalLoader::loadAidl() {
std::lock_guard<std::mutex> lock(gHalMutex);
static bool gHalExists = true;
- static auto loadFn = []() { return waitForVintfService<IPower>(); };
- return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+ if (!gHalExists) {
+ return nullptr;
+ }
+ if (gHalAidl) {
+ return gHalAidl;
+ }
+ auto aidlServiceName =
+ std::string(aidl::android::hardware::power::IPower::descriptor) + "/default";
+ if (!AServiceManager_isDeclared(aidlServiceName.c_str())) {
+ gHalExists = false;
+ return nullptr;
+ }
+ gHalAidl = aidl::android::hardware::power::IPower::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(aidlServiceName.c_str())));
+ if (gHalAidl) {
+ ALOGI("Successfully connected to Power HAL AIDL service.");
+ } else {
+ ALOGI("Power HAL AIDL service not available.");
+ gHalExists = false;
+ }
+ return gHalAidl;
}
sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 9e7adf8..76afbfc 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -15,86 +15,49 @@
*/
#define LOG_TAG "HalWrapper"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
#include <cinttypes>
using namespace android::hardware::power;
-namespace Aidl = android::hardware::power;
+namespace Aidl = aidl::android::hardware::power;
namespace android {
namespace power {
// -------------------------------------------------------------------------------------------------
-
-inline HalResult<void> toHalResult(const binder::Status& result) {
+inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) {
if (result.isOk()) {
return HalResult<void>::ok();
}
- ALOGE("Power HAL request failed: %s", result.toString8().c_str());
- return HalResult<void>::fromStatus(result);
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
- return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
- return ret.isOk() ? HalResult<T>::fromStatus(status, data)
- : HalResult<T>::failed(ret.description());
+ ALOGE("Power HAL request failed: %s", result.getDescription().c_str());
+ return HalResult<void>::failed(result.getDescription());
}
// -------------------------------------------------------------------------------------------------
-HalResult<void> HalResult<void>::fromStatus(status_t status) {
- if (status == android::OK) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(statusToString(status));
-}
-
-HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.toString8().c_str()));
-}
-
-template <typename R>
-HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
-}
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
toString(boost).c_str(), durationMs);
return HalResult<void>::unsupported();
}
-HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
enabled ? "true" : "false");
return HalResult<void>::unsupported();
}
-HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
threadIds.size());
- return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
}
HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
@@ -104,8 +67,8 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
- if (boost == Boost::INTERACTION) {
+HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) {
+ if (boost == Aidl::Boost::INTERACTION) {
return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs);
} else {
ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
@@ -113,20 +76,20 @@
}
}
-HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_0::setMode(Aidl::Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
switch (mode) {
- case Mode::LAUNCH:
+ case Aidl::Mode::LAUNCH:
return sendPowerHint(V1_3::PowerHint::LAUNCH, data);
- case Mode::LOW_POWER:
+ case Aidl::Mode::LOW_POWER:
return sendPowerHint(V1_3::PowerHint::LOW_POWER, data);
- case Mode::SUSTAINED_PERFORMANCE:
+ case Aidl::Mode::SUSTAINED_PERFORMANCE:
return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data);
- case Mode::VR:
+ case Aidl::Mode::VR:
return sendPowerHint(V1_3::PowerHint::VR_MODE, data);
- case Mode::INTERACTIVE:
+ case Aidl::Mode::INTERACTIVE:
return setInteractive(enabled);
- case Mode::DOUBLE_TAP_TO_WAKE:
+ case Aidl::Mode::DOUBLE_TAP_TO_WAKE:
return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
default:
ALOGV("Skipped setMode %s because Power HAL AIDL not available",
@@ -150,11 +113,11 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
threadIds.size());
- return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
}
HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
@@ -178,26 +141,26 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HidlHalWrapperV1_2::setBoost(Aidl::Boost boost, int32_t durationMs) {
switch (boost) {
- case Boost::CAMERA_SHOT:
+ case Aidl::Boost::CAMERA_SHOT:
return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs);
- case Boost::CAMERA_LAUNCH:
+ case Aidl::Boost::CAMERA_LAUNCH:
return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs);
default:
return HidlHalWrapperV1_1::setBoost(boost, durationMs);
}
}
-HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_2::setMode(Aidl::Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
switch (mode) {
- case Mode::CAMERA_STREAMING_SECURE:
- case Mode::CAMERA_STREAMING_LOW:
- case Mode::CAMERA_STREAMING_MID:
- case Mode::CAMERA_STREAMING_HIGH:
+ case Aidl::Mode::CAMERA_STREAMING_SECURE:
+ case Aidl::Mode::CAMERA_STREAMING_LOW:
+ case Aidl::Mode::CAMERA_STREAMING_MID:
+ case Aidl::Mode::CAMERA_STREAMING_HIGH:
return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data);
- case Mode::AUDIO_STREAMING_LOW_LATENCY:
+ case Aidl::Mode::AUDIO_STREAMING_LOW_LATENCY:
return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data);
default:
return HidlHalWrapperV1_1::setMode(mode, enabled);
@@ -206,9 +169,9 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_3::setMode(Aidl::Mode mode, bool enabled) {
uint32_t data = enabled ? 1 : 0;
- if (mode == Mode::EXPENSIVE_RENDERING) {
+ if (mode == Aidl::Mode::EXPENSIVE_RENDERING) {
return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data);
}
return HidlHalWrapperV1_2::setMode(mode, enabled);
@@ -222,7 +185,7 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
std::unique_lock<std::mutex> lock(mBoostMutex);
size_t idx = static_cast<size_t>(boost);
@@ -237,7 +200,7 @@
auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
if (!isSupportedRet.isOk()) {
ALOGE("Skipped setBoost %s because check support failed with: %s",
- toString(boost).c_str(), isSupportedRet.toString8().c_str());
+ toString(boost).c_str(), isSupportedRet.getDescription().c_str());
// return HalResult::FAILED;
return HalResult<void>::fromStatus(isSupportedRet);
}
@@ -254,7 +217,7 @@
return toHalResult(mHandle->setBoost(boost, durationMs));
}
-HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
std::unique_lock<std::mutex> lock(mModeMutex);
size_t idx = static_cast<size_t>(mode);
@@ -268,7 +231,7 @@
bool isSupported = false;
auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
if (!isSupportedRet.isOk()) {
- return HalResult<void>::failed(isSupportedRet.toString8().c_str());
+ return HalResult<void>::failed(isSupportedRet.getDescription());
}
mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
@@ -283,10 +246,10 @@
return toHalResult(mHandle->setMode(mode, enabled));
}
-HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
- sp<IPowerHintSession> appSession;
- return HalResult<sp<Aidl::IPowerHintSession>>::
+ std::shared_ptr<Aidl::IPowerHintSession> appSession;
+ return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
appSession);
}
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 4343aec..03fc38d 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -32,6 +32,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libpowermanager",
@@ -40,7 +41,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 6e5e14d..759485f 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -16,21 +16,24 @@
#define LOG_TAG "PowerHalAidlBenchmarks"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
-#include <android/hardware/power/WorkDuration.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
#include <benchmark/benchmark.h>
#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <powermanager/PowerHalLoader.h>
#include <testUtil.h>
#include <chrono>
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
-using android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::WorkDuration;
+using android::power::PowerHalLoader;
using std::chrono::microseconds;
using namespace android;
@@ -63,15 +66,15 @@
template <class R, class... Args0, class... Args1>
static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...),
Args1&&... args1) {
- sp<IPower> hal = waitForVintfService<IPower>();
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
if (hal == nullptr) {
ALOGV("Power HAL not available, skipping test...");
return;
}
- binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
- if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ndk::ScopedAStatus ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
ALOGV("Power HAL does not support this operation, skipping test...");
return;
}
@@ -79,7 +82,7 @@
while (state.KeepRunning()) {
ret = (*hal.*fn)(std::forward<Args1>(args1)...);
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
if (delay > 0us) {
testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
}
@@ -90,9 +93,9 @@
template <class R, class... Args0, class... Args1>
static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...),
Args1&&... args1) {
- sp<IPower> pwHal = waitForVintfService<IPower>();
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
- if (pwHal == nullptr) {
+ if (hal == nullptr) {
ALOGV("Power HAL not available, skipping test...");
return;
}
@@ -100,32 +103,32 @@
// do not use tid from the benchmark process, use 1 for init
std::vector<int32_t> threadIds{1};
int64_t durationNanos = 16666666L;
- sp<IPowerHintSession> hal;
+ std::shared_ptr<IPowerHintSession> session;
- auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal);
+ auto status = hal->createHintSession(1, 0, threadIds, durationNanos, &session);
- if (hal == nullptr) {
+ if (session == nullptr) {
ALOGV("Power HAL doesn't support session, skipping test...");
return;
}
- binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
- if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ndk::ScopedAStatus ret = (*session.*fn)(std::forward<Args1>(args1)...);
+ if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
ALOGV("Power HAL does not support this operation, skipping test...");
return;
}
while (state.KeepRunning()) {
- ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ ret = (*session.*fn)(std::forward<Args1>(args1)...);
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
if (ONEWAY_API_DELAY > 0us) {
testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
.count());
}
state.ResumeTiming();
}
- hal->close();
+ session->close();
}
static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
@@ -155,16 +158,17 @@
int64_t durationNanos = 16666666L;
int32_t tgid = 999;
int32_t uid = 1001;
- sp<IPowerHintSession> appSession;
- sp<IPower> hal = waitForVintfService<IPower>();
+ std::shared_ptr<IPowerHintSession> appSession;
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
if (hal == nullptr) {
ALOGV("Power HAL not available, skipping test...");
return;
}
- binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
- if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ndk::ScopedAStatus ret =
+ hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
ALOGV("Power HAL does not support this operation, skipping test...");
return;
}
@@ -172,7 +176,7 @@
while (state.KeepRunning()) {
ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
appSession->close();
state.ResumeTiming();
}
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index f8abc7a..effddda 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -16,15 +16,15 @@
#define LOG_TAG "PowerHalControllerBenchmarks"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <benchmark/benchmark.h>
#include <powermanager/PowerHalController.h>
#include <testUtil.h>
#include <chrono>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::power::HalResult;
using android::power::PowerHalController;
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
index 167f3a6..111b5d7 100644
--- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -16,10 +16,10 @@
#define LOG_TAG "PowerHalHidlBenchmarks"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <benchmark/benchmark.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
@@ -27,8 +27,6 @@
#include <chrono>
using android::hardware::Return;
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::PowerHint;
using std::chrono::microseconds;
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 54dffcf..08fcdc8 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -43,6 +43,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libpowermanager",
@@ -51,9 +52,10 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
],
static_libs: [
"libgmock",
],
+ require_root: true,
}
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
index 6cc7a6f..01270ce 100644
--- a/services/powermanager/tests/PowerHalControllerTest.cpp
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "PowerHalControllerTest"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalController.h>
@@ -26,8 +26,8 @@
#include <thread>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::IPower;
using android::hardware::power::V1_0::PowerHint;
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
index e36deed..7d97354 100644
--- a/services/powermanager/tests/PowerHalLoaderTest.cpp
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -16,9 +16,8 @@
#define LOG_TAG "PowerHalLoaderTest"
-#include <android-base/logging.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/IPower.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalLoader.h>
@@ -28,7 +27,7 @@
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_2 = android::hardware::power::V1_2::IPower;
using IPowerV1_3 = android::hardware::power::V1_3::IPower;
-using IPowerAidl = android::hardware::power::IPower;
+using IPowerAidl = aidl::android::hardware::power::IPower;
using namespace android;
using namespace android::power;
@@ -37,30 +36,30 @@
// -------------------------------------------------------------------------------------------------
template <typename T>
-sp<T> loadHal();
+T loadHal();
template <>
-sp<IPowerAidl> loadHal<IPowerAidl>() {
+std::shared_ptr<IPowerAidl> loadHal<std::shared_ptr<IPowerAidl>>() {
return PowerHalLoader::loadAidl();
}
template <>
-sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+sp<IPowerV1_0> loadHal<sp<IPowerV1_0>>() {
return PowerHalLoader::loadHidlV1_0();
}
template <>
-sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+sp<IPowerV1_1> loadHal<sp<IPowerV1_1>>() {
return PowerHalLoader::loadHidlV1_1();
}
template <>
-sp<IPowerV1_2> loadHal<IPowerV1_2>() {
+sp<IPowerV1_2> loadHal<sp<IPowerV1_2>>() {
return PowerHalLoader::loadHidlV1_2();
}
template <>
-sp<IPowerV1_3> loadHal<IPowerV1_3>() {
+sp<IPowerV1_3> loadHal<sp<IPowerV1_3>>() {
return PowerHalLoader::loadHidlV1_3();
}
@@ -69,46 +68,47 @@
template <typename T>
class PowerHalLoaderTest : public Test {
public:
- sp<T> load() { return ::loadHal<T>(); }
+ T load() { return ::loadHal<T>(); }
void unload() { PowerHalLoader::unloadAll(); }
};
// -------------------------------------------------------------------------------------------------
-
-typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes;
+typedef ::testing::Types<std::shared_ptr<IPowerAidl>, sp<IPowerV1_0>, sp<IPowerV1_1>,
+ sp<IPowerV1_2>, sp<IPowerV1_3>>
+ PowerHalTypes;
TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
- sp<TypeParam> firstHal = this->load();
+ TypeParam firstHal = this->load();
if (firstHal == nullptr) {
ALOGE("Power HAL not available. Skipping test.");
return;
}
- sp<TypeParam> secondHal = this->load();
+ TypeParam secondHal = this->load();
ASSERT_EQ(firstHal, secondHal);
}
TYPED_TEST(PowerHalLoaderTest, TestUnload) {
- sp<TypeParam> firstHal = this->load();
+ TypeParam firstHal = this->load();
if (firstHal == nullptr) {
ALOGE("Power HAL not available. Skipping test.");
return;
}
this->unload();
- sp<TypeParam> secondHal = this->load();
+ TypeParam secondHal = this->load();
ASSERT_NE(secondHal, nullptr);
ASSERT_NE(firstHal, secondHal);
}
TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
- std::vector<std::future<sp<TypeParam>>> futures;
+ std::vector<std::future<TypeParam>> futures;
for (int i = 0; i < 10; i++) {
futures.push_back(
std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
}
futures[0].wait();
- sp<TypeParam> firstHal = futures[0].get();
+ TypeParam firstHal = futures[0].get();
if (firstHal == nullptr) {
ALOGE("Power HAL not available. Skipping test.");
return;
@@ -116,7 +116,7 @@
for (int i = 1; i < 10; i++) {
futures[i].wait();
- sp<TypeParam> currentHal = futures[i].get();
+ TypeParam currentHal = futures[i].get();
ASSERT_EQ(firstHal, currentHal);
}
}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index cb1a77a..641ba9f 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "PowerHalWrapperAidlTest"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -28,11 +28,11 @@
#include <unistd.h>
#include <thread>
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
using android::binder::Status;
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
using namespace android;
using namespace android::power;
@@ -43,18 +43,21 @@
class MockIPower : public IPower {
public:
- MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
- MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
- MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
- MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
- MOCK_METHOD(Status, createHintSession,
+ MockIPower() = default;
+
+ MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos, sp<IPowerHintSession>* session),
+ int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
- MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
};
// -------------------------------------------------------------------------------------------------
@@ -65,13 +68,13 @@
protected:
std::unique_ptr<HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIPower>> mMockHal = nullptr;
+ std::shared_ptr<StrictMock<MockIPower>> mMockHal = nullptr;
};
// -------------------------------------------------------------------------------------------------
void PowerHalWrapperAidlTest::SetUp() {
- mMockHal = new StrictMock<MockIPower>();
+ mMockHal = ndk::SharedRefBase::make<StrictMock<MockIPower>>();
mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
ASSERT_NE(nullptr, mWrapper);
}
@@ -83,9 +86,11 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
@@ -97,13 +102,14 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
}
auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
@@ -115,7 +121,8 @@
TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(false),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
ASSERT_TRUE(result.isUnsupported());
@@ -128,8 +135,13 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
- EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+ auto& exp = EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+ .Times(Exactly(10));
+ for (int i = 0; i < 10; i++) {
+ exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ }
}
std::vector<std::thread> threads;
@@ -147,9 +159,11 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
@@ -161,13 +175,14 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::fromExceptionCode(-1))));
}
auto result = mWrapper->setMode(Mode::LAUNCH, true);
@@ -179,14 +194,16 @@
TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(false),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
auto result = mWrapper->setMode(Mode::LAUNCH, true);
ASSERT_TRUE(result.isUnsupported());
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::CAMERA_STREAMING_HIGH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+ .WillOnce(DoAll(SetArgPointee<1>(false),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
}
@@ -196,8 +213,13 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
.Times(Exactly(1))
- .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
- EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+ .WillOnce(DoAll(SetArgPointee<1>(true),
+ Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+ auto& exp = EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false)))
+ .Times(Exactly(10));
+ for (int i = 0; i < 10; i++) {
+ exp.WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ }
}
std::vector<std::thread> threads;
@@ -217,7 +239,8 @@
int64_t durationNanos = 16666666L;
EXPECT_CALL(*mMockHal.get(),
createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
- .Times(Exactly(1));
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
ASSERT_TRUE(result.isOk());
}
@@ -230,13 +253,16 @@
EXPECT_CALL(*mMockHal.get(),
createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
.Times(Exactly(1))
- .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)));
+ .WillOnce(Return(testing::ByMove(
+ ndk::ScopedAStatus::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT))));
auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
ASSERT_TRUE(result.isFailed());
}
TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) {
- EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
+ .Times(Exactly(1))
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
auto result = mWrapper->getHintSessionPreferredRate();
ASSERT_TRUE(result.isOk());
int64_t rate = result.value();
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index 0cd2e22..461143b 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -16,17 +16,17 @@
#define LOG_TAG "PowerHalWrapperHidlV1_0Test"
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::IPower;
using android::hardware::power::V1_0::PowerHint;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index 32f84e2..79dd996 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -16,18 +16,18 @@
#define LOG_TAG "PowerHalWrapperHidlV1_1Test"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::PowerHint;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
index cf48409..aa6d6c7 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
@@ -16,18 +16,18 @@
#define LOG_TAG "PowerHalWrapperHidlV1_2Test"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.2/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
index 2c48537..a995afd 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
@@ -16,18 +16,18 @@
#define LOG_TAG "PowerHalWrapperHidlV1_3Test"
+#include <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/Mode.h>
#include <android/hardware/power/1.3/IPower.h>
-#include <android/hardware/power/Boost.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/Mode.h>
#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::hardware::power::V1_0::Feature;
using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 5c00260..16088ca 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -42,17 +42,20 @@
if (count > 0) {
for (size_t i=0 ; i<size_t(count) ; i++) {
- if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
- mAcc = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
- mMag = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
- mGyro = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
- uncalibratedGyro = Sensor(list + i);
+ // Only use non-wakeup sensors
+ if ((list[i].flags & SENSOR_FLAG_WAKE_UP) == 0) {
+ if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
+ mAcc = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
+ mMag = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
+ mGyro = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
+ uncalibratedGyro = Sensor(list + i);
+ }
}
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5683a92..326645e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -27,6 +27,7 @@
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
"librenderengine_deps",
+ "libtimestats_deps",
"surfaceflinger_defaults",
],
cflags: [
@@ -47,7 +48,7 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -58,14 +59,12 @@
"libGLESv2",
"libgui",
"libhidlbase",
- "liblayers_proto",
"liblog",
"libnativewindow",
"libpowermanager",
"libprocessgroup",
"libprotobuf-cpp-lite",
"libsync",
- "libtimestats",
"libui",
"libinput",
"libutils",
@@ -77,11 +76,13 @@
"libcompositionengine",
"libframetimeline",
"libgui_aidl_static",
+ "liblayers_proto",
"libperfetto_client_experimental",
"librenderengine",
"libscheduler",
"libserviceutils",
"libshaders",
+ "libtimestats",
"libtonemap",
],
header_libs: [
@@ -95,6 +96,7 @@
"libcompositionengine",
"librenderengine",
"libserviceutils",
+ "libtimestats",
],
export_shared_lib_headers: [
"android.hardware.graphics.allocator@2.0",
@@ -106,7 +108,6 @@
"android.hardware.graphics.composer@2.4",
"libpowermanager",
"libhidlbase",
- "libtimestats",
],
// TODO (marissaw): this library is not used by surfaceflinger. This is here so
// the library compiled in a way that is accessible to system partition when running
@@ -167,6 +168,7 @@
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
+ "HdrSdrRatioOverlay.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
@@ -213,14 +215,12 @@
"-DLOG_TAG=\"SurfaceFlinger\"",
],
shared_libs: [
- "android.frameworks.displayservice@1.0",
"android.hardware.configstore-utils",
"android.hardware.configstore@1.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"libbinder",
"libcutils",
- "libdisplayservicehidl",
"libhidlbase",
"liblog",
"libprocessgroup",
@@ -228,6 +228,8 @@
"libutils",
],
static_libs: [
+ "android.frameworks.displayservice@1.0",
+ "libdisplayservicehidl",
"libserviceutils",
],
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index f3a0186..3426495 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -12,6 +12,7 @@
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
"librenderengine_deps",
+ "libtimestats_deps",
"surfaceflinger_defaults",
],
cflags: [
@@ -26,22 +27,22 @@
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"libbase",
"libcutils",
"libgui",
- "liblayers_proto",
"liblog",
"libnativewindow",
"libprotobuf-cpp-lite",
"libSurfaceFlingerProp",
- "libtimestats",
"libui",
"libutils",
],
static_libs: [
+ "liblayers_proto",
"libmath",
"librenderengine",
+ "libtimestats",
"libtonemap",
"libaidlcommonsupport",
"libprocessgroup",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 09bc467..1a8644e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -67,9 +67,6 @@
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
- // If not Dataspace::UNKNOWN, overrides the dataspace on each output
- ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
-
// Forces a color mode on the outputs being refreshed
ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index df44e75..9c80cac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -87,10 +87,6 @@
// Returns true if HWC for this profile supports the dataspace
virtual bool isDataspaceSupported(ui::Dataspace) const = 0;
- // Returns the target dataspace for picked color mode and dataspace
- virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace,
- ui::Dataspace colorSpaceAgnosticDataspace) const = 0;
-
// Debugging
virtual void dump(std::string&) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a3d8639..370c7cf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -136,7 +136,6 @@
ui::ColorMode mode{ui::ColorMode::NATIVE};
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
- ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
};
// Use internally to incrementally compute visibility/coverage
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
index 9bc0e68..3aad04c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -55,7 +55,6 @@
const HdrCapabilities& getHdrCapabilities() const override;
bool isDataspaceSupported(ui::Dataspace) const override;
- ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override;
void dump(std::string&) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 28c6e92..6cb1e7e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -116,9 +116,6 @@
// Current active dataspace
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
- // Current target dataspace
- ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
-
std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
bool previousDeviceRequestedSuccess = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index d607c75..9f6141a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -66,7 +66,7 @@
TexturePool(renderengine::RenderEngine& renderEngine)
: mRenderEngine(renderEngine), mEnabled(false) {}
- virtual ~TexturePool() = default;
+ virtual ~TexturePool();
// Sets the display size for the texture pool.
// This will trigger a reallocation for all remaining textures in the pool.
@@ -83,11 +83,10 @@
// be held by the pool. This is useful when the active display changes.
void setEnabled(bool enable);
- void dump(std::string& out) const;
+ void dump(std::string& out) const EXCLUDES(mMutex);
protected:
// Proteted visibility so that they can be used for testing
- const static constexpr size_t kMinPoolSize = 3;
const static constexpr size_t kMaxPoolSize = 4;
struct Entry {
@@ -96,16 +95,20 @@
};
std::deque<Entry> mPool;
+ std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture;
private:
- std::shared_ptr<renderengine::ExternalTexture> genTexture();
+ std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size);
// Returns a previously borrowed texture to the pool.
void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
const sp<Fence>& fence);
- void allocatePool();
- renderengine::RenderEngine& mRenderEngine;
- ui::Size mSize;
+ void genTextureAsyncIfNeeded() REQUIRES(mMutex);
+ void resetPool() REQUIRES(mMutex);
+ renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex);
+ ui::Size mSize GUARDED_BY(mMutex);
bool mEnabled;
+ mutable std::mutex mMutex;
+ mutable std::mutex mRenderEngineMutex;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
index 1aaebea..a904521 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -43,8 +43,6 @@
MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace));
- MOCK_CONST_METHOD3(getTargetDataspace,
- ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 85fc095..f2acfc9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -107,14 +107,9 @@
}
void Display::setColorProfile(const ColorProfile& colorProfile) {
- const ui::Dataspace targetDataspace =
- getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
- colorProfile.colorSpaceAgnosticDataspace);
-
if (colorProfile.mode == getState().colorMode &&
colorProfile.dataspace == getState().dataspace &&
- colorProfile.renderIntent == getState().renderIntent &&
- targetDataspace == getState().targetDataspace) {
+ colorProfile.renderIntent == getState().renderIntent) {
return;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index a7c4512..8f67f36 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -391,17 +391,6 @@
}
}
-ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace,
- Dataspace colorSpaceAgnosticDataspace) const {
- if (isHdrColorMode(mode)) {
- return Dataspace::UNKNOWN;
- }
- if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) {
- return colorSpaceAgnosticDataspace;
- }
- return dataspace;
-}
-
void DisplayColorProfile::dump(std::string& out) const {
out.append(" Composition Display Color State:");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 1205a2c..d4230f5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -261,22 +261,16 @@
}
void Output::setColorProfile(const ColorProfile& colorProfile) {
- ui::Dataspace targetDataspace =
- getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
- colorProfile.colorSpaceAgnosticDataspace);
-
auto& outputState = editState();
if (outputState.colorMode == colorProfile.mode &&
outputState.dataspace == colorProfile.dataspace &&
- outputState.renderIntent == colorProfile.renderIntent &&
- outputState.targetDataspace == targetDataspace) {
+ outputState.renderIntent == colorProfile.renderIntent) {
return;
}
outputState.colorMode = colorProfile.mode;
outputState.dataspace = colorProfile.dataspace;
outputState.renderIntent = colorProfile.renderIntent;
- outputState.targetDataspace = targetDataspace;
mRenderSurface->setBufferDataspace(colorProfile.dataspace);
@@ -469,15 +463,14 @@
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
- ATRACE_CALL();
- ALOGV(__FUNCTION__);
-
auto& outputState = editState();
// Do nothing if this output is not enabled or there is no need to perform this update
if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
return;
}
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
// Process the layers to determine visibility and coverage
compositionengine::Output::CoverageState coverage{layerFESet};
@@ -985,8 +978,7 @@
const compositionengine::CompositionRefreshArgs& refreshArgs) const {
if (refreshArgs.outputColorSetting == OutputColorSetting::kUnmanaged) {
return ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC,
- refreshArgs.colorSpaceAgnosticDataspace};
+ ui::RenderIntent::COLORIMETRIC};
}
ui::Dataspace hdrDataSpace;
@@ -1032,8 +1024,7 @@
mDisplayColorProfile->getBestColorMode(bestDataSpace, intent, &outDataSpace, &outMode,
&outRenderIntent);
- return ColorProfile{outMode, outDataSpace, outRenderIntent,
- refreshArgs.colorSpaceAgnosticDataspace};
+ return ColorProfile{outMode, outDataSpace, outRenderIntent};
}
void Output::beginFrame() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 9713e79..39cf671 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -52,7 +52,6 @@
dumpVal(out, "colorMode", toString(colorMode), colorMode);
dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
- dumpVal(out, "targetDataspace", toString(targetDataspace), targetDataspace);
out.append("\n ");
dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0ac0ecb..b492b6a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -315,9 +315,10 @@
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
// avoid the need for color conversion.
- state.dataspace = layerFEState->isColorspaceAgnostic &&
- outputState.targetDataspace != ui::Dataspace::UNKNOWN
- ? outputState.targetDataspace
+ // For now, also respect the colorspace agnostic flag if we're drawing to HDR, to avoid drastic
+ // luminance shift. TODO(b/292162273): we should check if that's true though.
+ state.dataspace = layerFEState->isColorspaceAgnostic && !isHdrDataspace(outputState.dataspace)
+ ? outputState.dataspace
: layerFEState->dataspace;
// Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index 54ecb56..10f58ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -25,31 +25,61 @@
namespace android::compositionengine::impl::planner {
-void TexturePool::allocatePool() {
+TexturePool::~TexturePool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
+}
+
+void TexturePool::resetPool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
mPool.clear();
- if (mEnabled && mSize.isValid()) {
- mPool.resize(kMinPoolSize);
- std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
- return Entry{genTexture(), nullptr};
- });
+ genTextureAsyncIfNeeded();
+}
+
+// Generate a new texture asynchronously so it will not require allocation on the main
+// thread.
+void TexturePool::genTextureAsyncIfNeeded() {
+ if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) {
+ mGenTextureFuture = std::async(
+ std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize);
}
}
void TexturePool::setDisplaySize(ui::Size size) {
+ std::lock_guard lock(mMutex);
if (mSize == size) {
return;
}
mSize = size;
- allocatePool();
+ resetPool();
}
std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
if (mPool.empty()) {
- return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
+ std::lock_guard lock(mMutex);
+ std::shared_ptr<TexturePool::AutoTexture> tex;
+ if (mGenTextureFuture.valid()) {
+ tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr);
+ } else {
+ tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr);
+ }
+ // Speculatively generate a new texture, so that the next call does not need
+ // to wait for allocation.
+ genTextureAsyncIfNeeded();
+ return tex;
}
const auto entry = mPool.front();
mPool.pop_front();
+ if (mPool.empty()) {
+ std::lock_guard lock(mMutex);
+ // Similiarly generate a new texture when lending out the last entry, so that
+ // the next call does not need to wait for allocation.
+ genTextureAsyncIfNeeded();
+ }
return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
}
@@ -60,6 +90,8 @@
return;
}
+ std::lock_guard lock(mMutex);
+
// Or the texture on the floor if the pool is no longer tracking textures of the same size.
if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
@@ -80,13 +112,14 @@
mPool.push_back({std::move(texture), fence});
}
-std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
- LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) {
+ std::lock_guard lock(mRenderEngineMutex);
+ LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size");
return std::make_shared<
renderengine::impl::
ExternalTexture>(sp<GraphicBuffer>::
- make(static_cast<uint32_t>(mSize.getWidth()),
- static_cast<uint32_t>(mSize.getHeight()),
+ make(static_cast<uint32_t>(size.getWidth()),
+ static_cast<uint32_t>(size.getHeight()),
HAL_PIXEL_FORMAT_RGBA_8888, 1U,
static_cast<uint64_t>(
GraphicBuffer::USAGE_HW_RENDER |
@@ -100,13 +133,16 @@
void TexturePool::setEnabled(bool enabled) {
mEnabled = enabled;
- allocatePool();
+
+ std::lock_guard lock(mMutex);
+ resetPool();
}
void TexturePool::dump(std::string& out) const {
+ std::lock_guard lock(mMutex);
base::StringAppendF(&out,
"TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
}
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 21b9aa9..b3ff2ec 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -646,29 +646,5 @@
EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
}
-/*
- * RenderSurface::getTargetDataspace()
- */
-
-TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) {
- auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
-
- // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override,
- // the input dataspace should be returned.
- EXPECT_EQ(Dataspace::DISPLAY_P3,
- profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
- Dataspace::UNKNOWN));
-
- // If colorSpaceAgnosticDataspace is set, its value should be returned
- EXPECT_EQ(Dataspace::V0_SRGB,
- profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
- Dataspace::V0_SRGB));
-
- // For an HDR colorspace, Dataspace::UNKNOWN should be returned.
- EXPECT_EQ(Dataspace::UNKNOWN,
- profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ,
- Dataspace::UNKNOWN));
-}
-
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 9be6bc2..027004a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -403,23 +403,18 @@
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
- EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
- .WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
-
// These values are expected to be the initial state.
ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
- ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are unchanged, nothing happens
mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are different, updates happen
EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -429,13 +424,11 @@
.Times(1);
mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
}
TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
@@ -448,19 +441,13 @@
virtualDisplay->setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(colorProfile));
- EXPECT_CALL(*colorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
-
- virtualDisplay->setColorProfile(
- ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ virtualDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3,
+ ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 67b94ee..892bb8f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -94,7 +94,8 @@
MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
- MOCK_CONST_METHOD1(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId));
+ MOCK_CONST_METHOD2(getModes,
+ std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t));
MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index aa83883..9039d16 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -642,7 +642,7 @@
TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
- mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
+ mOutputState.dataspace = ui::Dataspace::V0_SCRGB;
// If the layer is not colorspace agnostic, the output layer dataspace
// should use the layers requested colorspace.
@@ -659,11 +659,18 @@
mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
+
+ // If the output is HDR, then don't blind the user with a colorspace agnostic dataspace
+ // drawing all white
+ mOutputState.dataspace = ui::Dataspace::BT2020_PQ;
+
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
}
TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceWith170mReplacement) {
mLayerFEState.dataspace = ui::Dataspace::TRANSFER_SMPTE_170M;
- mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
mOutputState.treat170mAsSrgb = false;
mLayerFEState.isColorspaceAgnostic = false;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 9e0e7b5..ebf9a2b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -175,12 +175,10 @@
using ColorProfile = compositionengine::Output::ColorProfile;
void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
- android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+ android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d]) ", name,
toString(profile.mode).c_str(), profile.mode,
toString(profile.dataspace).c_str(), profile.dataspace,
- toString(profile.renderIntent).c_str(), profile.renderIntent,
- toString(profile.colorSpaceAgnosticDataspace).c_str(),
- profile.colorSpaceAgnosticDataspace);
+ toString(profile.renderIntent).c_str(), profile.renderIntent);
}
// Checks for a ColorProfile match
@@ -192,8 +190,7 @@
*result_listener << buf;
return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
- (expected.renderIntent == arg.renderIntent) &&
- (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+ (expected.renderIntent == arg.renderIntent);
}
/*
@@ -540,20 +537,14 @@
TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
using ColorProfile = Output::ColorProfile;
- EXPECT_CALL(*mDisplayColorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace);
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
@@ -561,19 +552,12 @@
TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
using ColorProfile = Output::ColorProfile;
- EXPECT_CALL(*mDisplayColorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
-
mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3;
mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3;
mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
- mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN;
mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
@@ -2133,12 +2117,11 @@
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput,
- setColorProfile(ColorProfileEq(
- ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+ setColorProfile(
+ ColorProfileEq(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC})));
mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mOutput.updateColorProfile(mRefreshArgs);
}
@@ -2148,7 +2131,6 @@
OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
}
struct ExpectBestColorModeCallResultUsedToSetColorProfileState
@@ -2163,8 +2145,7 @@
SetArgPointee<4>(renderIntent)));
EXPECT_CALL(getInstance()->mOutput,
setColorProfile(
- ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
- ui::Dataspace::UNKNOWN})));
+ ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent})));
return nextState<ExecuteState>();
}
};
@@ -2191,55 +2172,6 @@
.execute();
}
-struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
- : public OutputUpdateColorProfileTest {
- OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
- EXPECT_CALL(*mDisplayColorProfile,
- getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
- .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
- SetArgPointee<3>(ui::ColorMode::NATIVE),
- SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
- mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- }
-
- struct IfColorSpaceAgnosticDataspaceSetToState
- : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
- [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
- getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
- return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
- }
- };
-
- struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
- : public CallOrderStateMachineHelper<
- TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
- [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
- ui::Dataspace dataspace) {
- EXPECT_CALL(getInstance()->mOutput,
- setColorProfile(ColorProfileEq(
- ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, dataspace})));
- return nextState<ExecuteState>();
- }
- };
-
- // Call this member function to start using the mini-DSL defined above.
- [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
-};
-
-TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
- verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
- .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
- .execute();
-}
-
-TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
- verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
- .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
- .execute();
-}
-
struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
: public OutputUpdateColorProfileTest {
// Internally the implementation looks through the dataspaces of all the
@@ -2248,7 +2180,6 @@
OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
@@ -2368,7 +2299,6 @@
OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
@@ -2424,7 +2354,6 @@
struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
OutputUpdateColorProfileTest_Hdr() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
}
@@ -2703,7 +2632,6 @@
OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index 6fc90fe..494a9f4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -32,9 +32,9 @@
public:
TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {}
- size_t getMinPoolSize() const { return kMinPoolSize; }
size_t getMaxPoolSize() const { return kMaxPoolSize; }
size_t getPoolSize() const { return mPool.size(); }
+ size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); }
};
struct TexturePoolTest : public testing::Test {
@@ -56,16 +56,8 @@
TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine);
};
-TEST_F(TexturePoolTest, preallocatesMinPool) {
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
-}
-
-TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) {
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) {
- auto texture = mTexturePool.borrowTexture();
- }
-
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+TEST_F(TexturePoolTest, preallocatesZeroSizePool) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) {
@@ -119,10 +111,10 @@
static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
mTexturePool.setDisplaySize(kDisplaySizeTwo);
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture.reset();
// When the texture is returned to the pool, the pool now destroys it.
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture = mTexturePool.borrowTexture();
EXPECT_EQ(kDisplaySizeTwo.getWidth(),
@@ -132,14 +124,11 @@
}
TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
-
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
- EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
@@ -148,12 +137,11 @@
}
TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
@@ -162,12 +150,13 @@
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
-TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+TEST_F(TexturePoolTest, genFutureWhenReEnabled) {
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_FALSE(mTexturePool.isGenTextureFutureValid());
mTexturePool.setEnabled(true);
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_TRUE(mTexturePool.isGenTextureFutureValid());
}
} // namespace
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f6ca9e2..32bd890 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -37,11 +37,11 @@
#include <configstore/Utils.h>
#include <log/log.h>
#include <system/window.h>
-#include <ui/GraphicTypes.h>
#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
#include "FrontEnd/DisplayInfo.h"
+#include "HdrSdrRatioOverlay.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
#include "SurfaceFlinger.h"
@@ -261,6 +261,9 @@
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setLayerStack(filter.layerStack);
}
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->setLayerStack(filter.layerStack);
+ }
}
void DisplayDevice::setFlags(uint32_t flags) {
@@ -274,10 +277,14 @@
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setViewport(size);
}
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->setViewport(size);
+ }
}
void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
Rect orientedDisplaySpaceRect) {
+ mIsOrientationChanged = mOrientation != orientation;
mOrientation = orientation;
// We need to take care of display rotation for globalTransform for case if the panel is not
@@ -411,6 +418,26 @@
capabilities.getDesiredMinLuminance());
}
+void DisplayDevice::enableHdrSdrRatioOverlay(bool enable) {
+ if (!enable) {
+ mHdrSdrRatioOverlay.reset();
+ return;
+ }
+
+ mHdrSdrRatioOverlay = std::make_unique<HdrSdrRatioOverlay>();
+ mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
+ mHdrSdrRatioOverlay->setViewport(getSize());
+ updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+}
+
+void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) {
+ ATRACE_CALL();
+ mHdrSdrRatio = currentHdrSdrRatio;
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->changeHdrSdrRatio(currentHdrSdrRatio);
+ }
+}
+
void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
bool showRenderRate, bool showInMiddle) {
if (!enable) {
@@ -463,10 +490,23 @@
return false;
}
-void DisplayDevice::animateRefreshRateOverlay() {
+void DisplayDevice::animateOverlay() {
if (mRefreshRateOverlay) {
mRefreshRateOverlay->animate();
}
+ if (mHdrSdrRatioOverlay) {
+ // hdr sdr ratio is designed to be on the top right of the screen,
+ // therefore, we need to re-calculate the display's width and height
+ if (mIsOrientationChanged) {
+ auto width = getWidth();
+ auto height = getHeight();
+ if (mOrientation == ui::ROTATION_90 || mOrientation == ui::ROTATION_270) {
+ std::swap(width, height);
+ }
+ mHdrSdrRatioOverlay->setViewport({width, height});
+ }
+ mHdrSdrRatioOverlay->animate();
+ }
}
auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index dc5f8a8..e92125a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -55,6 +55,7 @@
class Fence;
class HWComposer;
+class HdrSdrRatioOverlay;
class IGraphicBufferProducer;
class Layer;
class RefreshRateOverlay;
@@ -235,13 +236,19 @@
return mRefreshRateSelector;
}
+ void animateOverlay();
+
// Enables an overlay to be displayed with the current refresh rate
void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
bool showInMiddle) REQUIRES(kMainThreadContext);
void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
- void animateRefreshRateOverlay();
+
+ // Enables an overlay to be display with the hdr/sdr ratio
+ void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
+ void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio);
+ bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; }
nsecs_t getVsyncPeriodFromHWC() const;
@@ -271,6 +278,7 @@
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
+ bool mIsOrientationChanged = false;
// Allow nullopt as initial power mode.
using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
@@ -297,6 +305,9 @@
std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+ std::unique_ptr<HdrSdrRatioOverlay> mHdrSdrRatioOverlay;
+ // This parameter is only used for hdr/sdr ratio overlay
+ float mHdrSdrRatio = 1.0f;
mutable std::mutex mActiveModeLock;
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index c0eb36d..1f409c6 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -244,14 +244,13 @@
addReader(translate<Display>(kSingleReaderKey));
// If unable to read interface version, then become backwards compatible.
- int32_t version = 1;
- const auto status = mAidlComposerClient->getInterfaceVersion(&version);
+ const auto status = mAidlComposerClient->getInterfaceVersion(&mComposerInterfaceVersion);
if (!status.isOk()) {
ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
status.getDescription().c_str());
}
- mSupportsBufferSlotsToClear = version > 1;
- if (!mSupportsBufferSlotsToClear) {
+
+ if (mComposerInterfaceVersion <= 1) {
if (sysprop::clear_slots_with_set_layer_buffer(false)) {
mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
GraphicBuffer::USAGE_HW_COMPOSER |
@@ -281,6 +280,10 @@
}
}
+bool AidlComposer::getDisplayConfigurationsSupported() const {
+ return mComposerInterfaceVersion >= 3;
+}
+
std::vector<Capability> AidlComposer::getCapabilities() {
std::vector<Capability> capabilities;
const auto status = mAidlComposer->getCapabilities(&capabilities);
@@ -489,6 +492,19 @@
return Error::NONE;
}
+Error AidlComposer::getDisplayConfigurations(Display display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>* outConfigs) {
+ const auto status =
+ mAidlComposerClient->getDisplayConfigurations(translate<int64_t>(display),
+ maxFrameIntervalNs, outConfigs);
+ if (!status.isOk()) {
+ ALOGE("getDisplayConfigurations failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ return Error::NONE;
+}
+
Error AidlComposer::getDisplayName(Display display, std::string* outName) {
const auto status = mAidlComposerClient->getDisplayName(translate<int64_t>(display), outName);
if (!status.isOk()) {
@@ -848,7 +864,7 @@
Error error = Error::NONE;
mMutex.lock_shared();
if (auto writer = getWriter(display)) {
- if (mSupportsBufferSlotsToClear) {
+ if (mComposerInterfaceVersion > 1) {
writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
translate<int64_t>(layer), slotsToClear);
// Backwards compatible way of clearing buffer slots is to set the layer buffer with a
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 8d21b49..b1b57a4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -66,6 +66,7 @@
~AidlComposer() override;
bool isSupported(OptionalFeature) const;
+ bool getDisplayConfigurationsSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -95,6 +96,8 @@
Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue) override;
Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayConfigurations(Display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>*);
Error getDisplayName(Display display, std::string* outName) override;
Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
@@ -285,8 +288,8 @@
// threading annotations.
ftl::SharedMutex mMutex;
- // Whether or not explicitly clearing buffer slots is supported.
- bool mSupportsBufferSlotsToClear;
+ int32_t mComposerInterfaceVersion = 1;
+
// Buffer slots for layers are cleared by setting the slot buffer to this buffer.
sp<GraphicBuffer> mClearSlotBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index cf67795..e942587 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -39,6 +39,7 @@
#include <aidl/android/hardware/graphics/composer3/Color.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
@@ -85,6 +86,7 @@
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using DisplayConfiguration = V3_0::DisplayConfiguration;
using aidl::android::hardware::graphics::common::Hdr;
class Composer {
@@ -103,6 +105,7 @@
};
virtual bool isSupported(OptionalFeature) const = 0;
+ virtual bool getDisplayConfigurationsSupported() const = 0;
virtual std::vector<aidl::android::hardware::graphics::composer3::Capability>
getCapabilities() = 0;
@@ -130,6 +133,10 @@
virtual Error getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) = 0;
virtual Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs) = 0;
+
+ virtual Error getDisplayConfigurations(Display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>*) = 0;
+
virtual Error getDisplayName(Display display, std::string* outName) = 0;
virtual Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 61a9a08..1810925 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -80,20 +80,20 @@
return *this;
}
- Builder& setDpiX(int32_t dpiX) {
- if (dpiX == -1) {
+ Builder& setDpiX(float dpiX) {
+ if (dpiX == -1.f) {
mDisplayMode->mDpi.x = getDefaultDensity();
} else {
- mDisplayMode->mDpi.x = dpiX / 1000.f;
+ mDisplayMode->mDpi.x = dpiX;
}
return *this;
}
- Builder& setDpiY(int32_t dpiY) {
- if (dpiY == -1) {
+ Builder& setDpiY(float dpiY) {
+ if (dpiY == -1.f) {
mDisplayMode->mDpi.y = getDefaultDensity();
} else {
- mDisplayMode->mDpi.y = dpiY / 1000.f;
+ mDisplayMode->mDpi.y = dpiY;
}
return *this;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f350eba..3177b33 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -261,10 +261,51 @@
return mDisplayData.count(displayId) && mDisplayData.at(displayId).hwcDisplay->isConnected();
}
-std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId,
+ int32_t maxFrameIntervalNs) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId();
+
+ if (mComposer->getDisplayConfigurationsSupported()) {
+ return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs);
+ }
+
+ return getModesFromLegacyDisplayConfigs(hwcDisplayId);
+}
+
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations(
+ uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const {
+ std::vector<hal::DisplayConfiguration> configs;
+ auto error = static_cast<hal::Error>(
+ mComposer->getDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs, &configs));
+ RETURN_IF_HWC_ERROR_FOR("getDisplayConfigurations", error, *toPhysicalDisplayId(hwcDisplayId),
+ {});
+
+ std::vector<HWCDisplayMode> modes;
+ modes.reserve(configs.size());
+ for (auto config : configs) {
+ auto hwcMode = HWCDisplayMode{
+ .hwcId = static_cast<hal::HWConfigId>(config.configId),
+ .width = config.width,
+ .height = config.height,
+ .vsyncPeriod = config.vsyncPeriod,
+ .configGroup = config.configGroup,
+ };
+
+ if (config.dpi) {
+ hwcMode.dpiX = config.dpi->x;
+ hwcMode.dpiY = config.dpi->y;
+ }
+
+ modes.push_back(hwcMode);
+ }
+
+ return modes;
+}
+
+std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayConfigs(
+ uint64_t hwcDisplayId) const {
std::vector<hal::HWConfigId> configIds;
auto error = static_cast<hal::Error>(mComposer->getDisplayConfigs(hwcDisplayId, &configIds));
RETURN_IF_HWC_ERROR_FOR("getDisplayConfigs", error, *toPhysicalDisplayId(hwcDisplayId), {});
@@ -272,17 +313,25 @@
std::vector<HWCDisplayMode> modes;
modes.reserve(configIds.size());
for (auto configId : configIds) {
- modes.push_back(HWCDisplayMode{
+ auto hwcMode = HWCDisplayMode{
.hwcId = configId,
.width = getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH),
.height = getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT),
.vsyncPeriod = getAttribute(hwcDisplayId, configId, hal::Attribute::VSYNC_PERIOD),
- .dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X),
- .dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y),
.configGroup = getAttribute(hwcDisplayId, configId, hal::Attribute::CONFIG_GROUP),
- });
- }
+ };
+ const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
+ const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
+ if (dpiX != -1) {
+ hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f;
+ }
+ if (dpiY != -1) {
+ hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f;
+ }
+
+ modes.push_back(hwcMode);
+ }
return modes;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3702c62..86f3825 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -99,8 +99,8 @@
int32_t width = -1;
int32_t height = -1;
nsecs_t vsyncPeriod = -1;
- int32_t dpiX = -1;
- int32_t dpiY = -1;
+ float dpiX = -1.f;
+ float dpiY = -1.f;
int32_t configGroup = -1;
friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
@@ -229,7 +229,8 @@
virtual bool isConnected(PhysicalDisplayId) const = 0;
- virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId) const = 0;
+ virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId,
+ int32_t maxFrameIntervalNs) const = 0;
virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0;
@@ -412,7 +413,8 @@
bool isConnected(PhysicalDisplayId) const override;
- std::vector<HWCDisplayMode> getModes(PhysicalDisplayId) const override;
+ std::vector<HWCDisplayMode> getModes(PhysicalDisplayId,
+ int32_t maxFrameIntervalNs) const override;
std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override;
@@ -501,6 +503,10 @@
std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+ std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId,
+ int32_t maxFrameIntervalNs) const;
+ std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const;
+
int32_t getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
hal::Attribute attribute) const;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index bf3089f..e95ae89 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#define ERROR_HAS_CHANGES 5
@@ -34,6 +35,7 @@
namespace V2_2 = android::hardware::graphics::composer::V2_2;
namespace V2_3 = android::hardware::graphics::composer::V2_3;
namespace V2_4 = android::hardware::graphics::composer::V2_4;
+namespace V3_0 = ::aidl::android::hardware::graphics::composer3;
using types::V1_0::ColorTransform;
using types::V1_0::Transform;
@@ -70,6 +72,7 @@
using Vsync = IComposerClient::Vsync;
using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
using Hdr = aidl::android::hardware::graphics::common::Hdr;
+using DisplayConfiguration = V3_0::DisplayConfiguration;
} // namespace hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 9b41da5..70d48de 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -269,6 +269,11 @@
}
}
+bool HidlComposer::getDisplayConfigurationsSupported() const {
+ // getDisplayConfigurations is not supported on the HIDL composer.
+ return false;
+};
+
std::vector<Capability> HidlComposer::getCapabilities() {
std::vector<Capability> capabilities;
mComposer->getCapabilities([&](const auto& tmpCapabilities) {
@@ -477,6 +482,12 @@
return error;
}
+Error HidlComposer::getDisplayConfigurations(Display, int32_t /*maxFrameIntervalNs*/,
+ std::vector<DisplayConfiguration>*) {
+ LOG_ALWAYS_FATAL("getDisplayConfigurations should not have been called on this, as "
+ "it's a HWC3 interface version 3 feature");
+}
+
Error HidlComposer::getDisplayName(Display display, std::string* outName) {
Error error = kDefaultError;
mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 0521acf..26d2222 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -167,6 +167,7 @@
~HidlComposer() override;
bool isSupported(OptionalFeature) const;
+ bool getDisplayConfigurationsSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -196,6 +197,8 @@
Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue) override;
Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayConfigurations(Display, int32_t maxFrameIntervalNs,
+ std::vector<DisplayConfiguration>*);
Error getDisplayName(Display display, std::string* outName) override;
Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 9c7576e..f00ef67 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -31,9 +31,9 @@
#include <utils/Mutex.h>
#include <utils/Trace.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <android/hardware/power/WorkDuration.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
#include <binder/IServiceManager.h>
@@ -49,11 +49,11 @@
namespace impl {
-using android::hardware::power::Boost;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
-using android::hardware::power::SessionHint;
-using android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::WorkDuration;
PowerAdvisor::~PowerAdvisor() = default;
@@ -230,7 +230,7 @@
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
- ret.exceptionMessage().c_str());
+ ret.getDescription().c_str());
mHintSessionRunning = false;
}
}
@@ -274,7 +274,7 @@
auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
if (!ret.isOk()) {
ALOGW("Failed to report actual work durations with error: %s",
- ret.exceptionMessage().c_str());
+ ret.getDescription().c_str());
mHintSessionRunning = false;
return;
}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index cfaa135..05e4c8b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -25,7 +25,7 @@
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <powermanager/PowerHalController.h>
#include <scheduler/Time.h>
@@ -247,13 +247,14 @@
bool mHintSessionRunning = false;
std::mutex mHintSessionMutex;
- sp<hardware::power::IPowerHintSession> mHintSession GUARDED_BY(mHintSessionMutex) = nullptr;
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
+ GUARDED_BY(mHintSessionMutex) = nullptr;
// Initialize to true so we try to call, to check if it's supported
bool mHasExpensiveRendering = true;
bool mHasDisplayUpdateImminent = true;
// Queue of actual durations saved to report
- std::vector<hardware::power::WorkDuration> mHintSessionQueue;
+ std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue;
// The latest values we have received for target and actual
Duration mTargetDuration = kDefaultTargetDuration;
std::optional<Duration> mActualDuration;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index ded734e..dcc29b9 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -140,6 +140,10 @@
janks.emplace_back("SurfaceFlinger Stuffing");
jankType &= ~JankType::SurfaceFlingerStuffing;
}
+ if (jankType & JankType::Dropped) {
+ janks.emplace_back("Dropped Frame");
+ jankType &= ~JankType::Dropped;
+ }
// jankType should be 0 if all types of jank were checked for.
LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -264,6 +268,11 @@
protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
jankType &= ~JankType::SurfaceFlingerStuffing;
}
+ if (jankType & JankType::Dropped) {
+ // Jank dropped does not append to other janks, it fully overrides.
+ protoJank |= FrameTimelineEvent::JANK_DROPPED;
+ jankType &= ~JankType::Dropped;
+ }
// jankType should be 0 if all types of jank were checked for.
LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -365,8 +374,7 @@
std::optional<int32_t> SurfaceFrame::getJankType() const {
std::scoped_lock lock(mMutex);
if (mPresentState == PresentState::Dropped) {
- // Return no jank if it's a dropped frame since we cannot attribute a jank to a it.
- return JankType::None;
+ return JankType::Dropped;
}
if (mActuals.presentTime == 0) {
// Frame hasn't been presented yet.
@@ -503,7 +511,8 @@
// We classify prediction expired as AppDeadlineMissed as the
// TokenManager::kMaxTokens we store is large enough to account for a
// reasonable app, so prediction expire would mean a huge scheduling delay.
- mJankType = JankType::AppDeadlineMissed;
+ mJankType = mPresentState != PresentState::Presented ? JankType::Dropped
+ : JankType::AppDeadlineMissed;
deadlineDelta = -1;
return;
}
@@ -594,17 +603,17 @@
mJankType |= displayFrameJankType;
}
}
+ if (mPresentState != PresentState::Presented) {
+ mJankType = JankType::Dropped;
+ // Since frame was not presented, lets drop any present value
+ mActuals.presentTime = 0;
+ }
}
void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
std::scoped_lock lock(mMutex);
- if (mPresentState != PresentState::Presented) {
- // No need to update dropped buffers
- return;
- }
-
mActuals.presentTime = presentTime;
nsecs_t deadlineDelta = 0;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 163d345..ab4c15d 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -149,13 +149,33 @@
return debug + "}";
}
-std::string LayerHierarchy::getDebugString(const char* prefix) const {
- std::string debug = prefix + getDebugStringShort();
- for (auto& [child, childVariant] : mChildren) {
- std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant);
- debug += "\n" + child->getDebugString(childPrefix.c_str());
+void LayerHierarchy::dump(std::ostream& out, const std::string& prefix,
+ LayerHierarchy::Variant variant, bool isLastChild) const {
+ if (!mLayer) {
+ out << " ROOT";
+ } else {
+ out << prefix + (isLastChild ? "└─ " : "├─ ");
+ if (variant == LayerHierarchy::Variant::Relative) {
+ out << "(Relative) ";
+ } else if (variant == LayerHierarchy::Variant::Mirror) {
+ out << "(Mirroring) " << *mLayer << "\n" + prefix + " └─ ...";
+ return;
+ }
+ out << *mLayer;
}
- return debug;
+
+ for (size_t i = 0; i < mChildren.size(); i++) {
+ auto& [child, childVariant] = mChildren[i];
+ if (childVariant == LayerHierarchy::Variant::Detached) continue;
+ const bool lastChild = i == (mChildren.size() - 1);
+ std::string childPrefix = prefix;
+ if (mLayer) {
+ childPrefix += (isLastChild ? " " : "│ ");
+ }
+ out << "\n";
+ child->dump(out, childPrefix, childVariant, lastChild);
+ }
+ return;
}
bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 5389ada..1e48387 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -156,7 +156,12 @@
const RequestedLayerState* getLayer() const;
const LayerHierarchy* getRelativeParent() const;
const LayerHierarchy* getParent() const;
- std::string getDebugString(const char* prefix = "") const;
+ friend std::ostream& operator<<(std::ostream& os, const LayerHierarchy& obj) {
+ std::string prefix = " ";
+ obj.dump(os, prefix, LayerHierarchy::Variant::Attached, /*isLastChild=*/false);
+ return os;
+ }
+
std::string getDebugStringShort() const;
// Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
// will contain the first relative root that was visited twice in a traversal.
@@ -172,6 +177,8 @@
void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
+ bool isLastChild) const;
const RequestedLayerState* mLayer;
LayerHierarchy* mParent = nullptr;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index f0826c6..d389a79 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -39,6 +39,63 @@
}
}
+std::ostream& operator<<(std::ostream& os, const ui::Transform& transform) {
+ const uint32_t type = transform.getType();
+ const uint32_t orientation = transform.getOrientation();
+ if (type == ui::Transform::IDENTITY) {
+ return os;
+ }
+
+ if (type & ui::Transform::UNKNOWN) {
+ std::string out;
+ transform.dump(out, "", "");
+ os << out;
+ return os;
+ }
+
+ if (type & ui::Transform::ROTATE) {
+ switch (orientation) {
+ case ui::Transform::ROT_0:
+ os << "ROT_0";
+ break;
+ case ui::Transform::FLIP_H:
+ os << "FLIP_H";
+ break;
+ case ui::Transform::FLIP_V:
+ os << "FLIP_V";
+ break;
+ case ui::Transform::ROT_90:
+ os << "ROT_90";
+ break;
+ case ui::Transform::ROT_180:
+ os << "ROT_180";
+ break;
+ case ui::Transform::ROT_270:
+ os << "ROT_270";
+ break;
+ case ui::Transform::ROT_INVALID:
+ default:
+ os << "ROT_INVALID";
+ break;
+ }
+ }
+
+ if (type & ui::Transform::SCALE) {
+ std::string out;
+ android::base::StringAppendF(&out, " scale x=%.4f y=%.4f ", transform.getScaleX(),
+ transform.getScaleY());
+ os << out;
+ }
+
+ if (type & ui::Transform::TRANSLATE) {
+ std::string out;
+ android::base::StringAppendF(&out, " tx=%.4f ty=%.4f ", transform.tx(), transform.ty());
+ os << out;
+ }
+
+ return os;
+}
+
} // namespace
LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
@@ -59,6 +116,7 @@
}
sequence = static_cast<int32_t>(state.id);
name = state.name;
+ debugName = state.debugName;
textureName = state.textureName;
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
@@ -73,6 +131,8 @@
? path
: LayerHierarchy::TraversalPath::ROOT;
reachablilty = LayerSnapshot::Reachablilty::Unreachable;
+ frameRateSelectionPriority = state.frameRateSelectionPriority;
+ layerMetadata = state.metadata;
}
// As documented in libhardware header, formats in the range
@@ -180,13 +240,13 @@
if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
if (invalidTransform) return "invalidTransform";
if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
- if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
+ if (!hasSomethingToDraw()) return "nothing to draw";
// visible
std::stringstream reason;
if (sidebandStream != nullptr) reason << " sidebandStream";
if (externalTexture != nullptr)
- reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber;
+ reason << " buffer=" << externalTexture->getId() << " frame=" << frameNumber;
if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
@@ -232,6 +292,36 @@
return debug.str();
}
+std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) {
+ out << "Layer [" << obj.path.id;
+ if (obj.path.mirrorRootId != UNASSIGNED_LAYER_ID) {
+ out << " mirrored from " << obj.path.mirrorRootId;
+ }
+ out << "] " << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible")
+ << " reason=" << obj.getIsVisibleReason();
+
+ if (!obj.geomLayerBounds.isEmpty()) {
+ out << "\n bounds={" << obj.transformedBounds.left << "," << obj.transformedBounds.top
+ << "," << obj.transformedBounds.bottom << "," << obj.transformedBounds.right << "}";
+ }
+
+ if (obj.geomLayerTransform.getType() != ui::Transform::IDENTITY) {
+ out << " toDisplayTransform={" << obj.geomLayerTransform << "}";
+ }
+
+ if (obj.hasInputInfo()) {
+ out << "\n input{"
+ << "(" << obj.inputInfo.inputConfig.string() << ")";
+ if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId;
+ if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop";
+ auto touchableRegion = obj.inputInfo.touchableRegion.getBounds();
+ out << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << ","
+ << touchableRegion.bottom << "," << touchableRegion.right << "}"
+ << "}";
+ }
+ return out;
+}
+
FloatRect LayerSnapshot::sourceBounds() const {
if (!externalTexture) {
return geomLayerBounds;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 2f45d52..1afcef9 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -41,10 +41,6 @@
}
};
-struct ChildState {
- bool hasValidFrameRate = false;
-};
-
// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
// passed to Render Engine are created using properties stored on this struct.
@@ -67,6 +63,7 @@
// generated from the same layer, for example when mirroring.
int32_t sequence;
std::string name;
+ std::string debugName;
uint32_t textureName;
bool contentOpaque;
bool layerOpaqueFlagSet;
@@ -93,12 +90,11 @@
ui::Transform::RotationFlags fixedTransformHint;
std::optional<ui::Transform::RotationFlags> transformHint;
bool handleSkipScreenshotFlag = false;
- int32_t frameRateSelectionPriority;
+ int32_t frameRateSelectionPriority = -1;
LayerHierarchy::TraversalPath mirrorRootPath;
uint32_t touchCropId;
gui::Uid uid = gui::Uid::INVALID;
gui::Pid pid = gui::Pid::INVALID;
- ChildState childState;
enum class Reachablilty : uint32_t {
// Can traverse the hierarchy from a root node and reach this snapshot
Reachable,
@@ -145,7 +141,7 @@
bool hasInputInfo() const;
FloatRect sourceBounds() const;
Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
-
+ friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
bool forceFullDamage, uint32_t displayRotationFlags);
};
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 21f0a67..23cfe928 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -29,6 +29,7 @@
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/Hal.h"
+#include "Layer.h" // eFrameRateSelectionPriority constants
#include "LayerLog.h"
#include "LayerSnapshotBuilder.h"
#include "TimeStats/TimeStats.h"
@@ -539,7 +540,7 @@
primaryDisplayRotationFlags);
snapshot->changes |= RequestedLayerState::Changes::Created;
}
- scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
+
if (traversalPath.isRelative()) {
bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
@@ -557,12 +558,9 @@
const LayerSnapshot& childSnapshot =
updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
depth + 1);
- updateChildState(*snapshot, childSnapshot, args);
+ updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, args);
}
- if (oldFrameRate == snapshot->frameRate) {
- snapshot->changes.clear(RequestedLayerState::Changes::FrameRate);
- }
return *snapshot;
}
@@ -665,36 +663,42 @@
}
}
-void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
- const LayerSnapshot& childSnapshot, const Args& args) {
- if (snapshot.childState.hasValidFrameRate) {
+void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
+ const LayerSnapshot& childSnapshot,
+ const Args& args) {
+ if (args.forceUpdate == ForceUpdateFlags::NONE &&
+ !args.layerLifecycleManager.getGlobalChanges().any(
+ RequestedLayerState::Changes::Hierarchy) &&
+ !childSnapshot.changes.any(RequestedLayerState::Changes::FrameRate) &&
+ !snapshot.changes.any(RequestedLayerState::Changes::FrameRate)) {
return;
}
- if (args.forceUpdate == ForceUpdateFlags::ALL ||
- childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
- // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes
- // for the same reason we are allowing touch boost for those layers. See
- // RefreshRateSelector::rankFrameRates for details.
- using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
- const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
- childSnapshot.frameRate.type == FrameRateCompatibility::Default;
- const auto layerVotedWithNoVote =
- childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
- const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
- childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
- snapshot.childState.hasValidFrameRate |= layerVotedWithDefaultCompatibility ||
- layerVotedWithNoVote || layerVotedWithExactCompatibility;
+ using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ if (snapshot.frameRate.rate.isValid() ||
+ snapshot.frameRate.type == FrameRateCompatibility::NoVote) {
+ // we already have a valid framerate.
+ return;
+ }
- // If we don't have a valid frame rate, but the children do, we set this
- // layer as NoVote to allow the children to control the refresh rate
- if (!snapshot.frameRate.rate.isValid() &&
- snapshot.frameRate.type != FrameRateCompatibility::NoVote &&
- snapshot.childState.hasValidFrameRate) {
- snapshot.frameRate =
- scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
- snapshot.changes |= childSnapshot.changes & RequestedLayerState::Changes::FrameRate;
- }
+ // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes
+ // for the same reason we are allowing touch boost for those layers. See
+ // RefreshRateSelector::rankFrameRates for details.
+ const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
+ childSnapshot.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote =
+ childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
+ const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
+ childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
+
+ bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+ layerVotedWithExactCompatibility;
+
+ // If we don't have a valid frame rate, but the children do, we set this
+ // layer as NoVote to allow the children to control the refresh rate
+ if (childHasValidFrameRate) {
+ snapshot.frameRate = scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
}
@@ -811,12 +815,24 @@
}
}
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::FrameRate)) {
+ if (forceUpdate ||
+ args.layerLifecycleManager.getGlobalChanges().any(
+ RequestedLayerState::Changes::Hierarchy) ||
+ snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
+ RequestedLayerState::Changes::Hierarchy)) {
snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
(requested.requestedFrameRate.type ==
scheduler::LayerInfo::FrameRateCompatibility::NoVote))
? requested.requestedFrameRate
: parentSnapshot.frameRate;
+ snapshot.changes |= RequestedLayerState::Changes::FrameRate;
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
+ snapshot.frameRateSelectionPriority =
+ (requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET)
+ ? parentSnapshot.frameRateSelectionPriority
+ : requested.frameRateSelectionPriority;
}
if (forceUpdate ||
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index c81a5d2..d361605 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -116,8 +116,8 @@
LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
const RequestedLayerState& layer,
const LayerSnapshot& parentSnapshot);
- void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
- const Args& args);
+ void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
+ const LayerSnapshot& childSnapshot, const Args& args);
void updateTouchableRegionCrop(const Args& args);
std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 5738262..a4777d1 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -19,6 +19,7 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
+#include <gui/TraceUtils.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <sys/types.h>
@@ -126,6 +127,16 @@
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
cachingHint = gui::CachingHint::Enabled;
+
+ if (name.length() > 77) {
+ std::string shortened;
+ shortened.append(name, 0, 36);
+ shortened.append("[...]");
+ shortened.append(name, name.length() - 36);
+ debugName = std::move(shortened);
+ } else {
+ debugName = name;
+ }
}
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
@@ -370,6 +381,13 @@
return debug.str();
}
+std::ostream& operator<<(std::ostream& out, const RequestedLayerState& obj) {
+ out << obj.debugName;
+ if (obj.relativeParentId != UNASSIGNED_LAYER_ID) out << " parent=" << obj.parentId;
+ if (!obj.handleAlive) out << " handleNotAlive";
+ return out;
+}
+
std::string RequestedLayerState::getDebugStringShort() const {
return "[" + std::to_string(id) + "]" + name;
}
@@ -507,6 +525,51 @@
return changes.test(Changes::Buffer) && !externalTexture;
}
+bool RequestedLayerState::backpressureEnabled() const {
+ return flags & layer_state_t::eEnableBackpressure;
+}
+
+bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
+ static constexpr uint64_t requiredFlags = layer_state_t::eBufferChanged;
+ if ((s.what & requiredFlags) != requiredFlags) {
+ ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+ (s.what | requiredFlags) & ~s.what);
+ return false;
+ }
+
+ static constexpr uint64_t deniedFlags = layer_state_t::eProducerDisconnect |
+ layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eFlagsChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged |
+ layer_state_t::eAutoRefreshChanged | layer_state_t::eReparent;
+ if (s.what & deniedFlags) {
+ ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
+ s.what & deniedFlags);
+ return false;
+ }
+
+ bool changedFlags = diff(s);
+ static constexpr auto deniedChanges = layer_state_t::ePositionChanged |
+ layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged |
+ layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::eCornerRadiusChanged | layer_state_t::eBackgroundBlurRadiusChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
+ layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
+ layer_state_t::eShadowRadiusChanged | layer_state_t::eFixedTransformHintChanged |
+ layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
+ layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged;
+ if (changedFlags & deniedChanges) {
+ ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
+ s.what & deniedChanges);
+ return false;
+ }
+
+ return true;
+}
+
void RequestedLayerState::clearChanges() {
what = 0;
changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 02e3bac..1c19d6d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -74,6 +74,7 @@
Rect getBufferCrop() const;
std::string getDebugString() const;
std::string getDebugStringShort() const;
+ friend std::ostream& operator<<(std::ostream& os, const RequestedLayerState& obj);
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
@@ -82,6 +83,8 @@
bool hasReadyFrame() const;
bool hasSidebandStreamFrame() const;
bool willReleaseBufferOnLatch() const;
+ bool backpressureEnabled() const;
+ bool isSimpleBufferUpdate(const layer_state_t&) const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
@@ -116,6 +119,7 @@
uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
uint64_t barrierFrameNumber = 0;
uint32_t barrierProducerId = 0;
+ std::string debugName;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index fa8eb3c..0d3c6eb 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -33,7 +33,7 @@
ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
}
-std::vector<TransactionState> TransactionHandler::flushTransactions() {
+void TransactionHandler::collectTransactions() {
while (!mLocklessTransactionQueue.isEmpty()) {
auto maybeTransaction = mLocklessTransactionQueue.pop();
if (!maybeTransaction.has_value()) {
@@ -42,7 +42,9 @@
auto transaction = maybeTransaction.value();
mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
}
+}
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
// Collect transaction that are ready to be applied.
std::vector<TransactionState> transactions;
TransactionFlushState flushState;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 865835f..04183bc 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -58,6 +58,8 @@
using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
bool hasPendingTransactions();
+ // Moves transactions from the lockless queue.
+ void collectTransactions();
std::vector<TransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
void queueTransaction(TransactionState&&);
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
new file mode 100644
index 0000000..2c0f518
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -0,0 +1,178 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// #define LOG_NDEBUG 0
+#include <algorithm>
+
+#include "HdrSdrRatioOverlay.h"
+
+#include <SkSurface.h>
+
+#undef LOG_TAG
+#define LOG_TAG "HdrSdrRatioOverlay"
+
+namespace android {
+
+void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) {
+ if (!isfinite(number) || number >= 10.f) return;
+ // We assume that the number range is [1.f, 10.f)
+ // and the decimal places are 2.
+ int value = static_cast<int>(number * 100);
+ SegmentDrawer::drawDigit(value / 100, left, color, canvas);
+
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas);
+ left += kDigitWidth + kDigitSpace;
+
+ SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas);
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawDigit(value % 10, left, color, canvas);
+}
+
+sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
+ ui::Transform::RotationFlags rotation) {
+ SkMatrix canvasTransform = SkMatrix();
+ const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> {
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ canvasTransform.setTranslate(kBufferHeight, 0);
+ canvasTransform.preRotate(90.f);
+ return {kBufferHeight, kBufferWidth};
+ case ui::Transform::ROT_270:
+ canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f);
+ return {kBufferHeight, kBufferWidth};
+ default:
+ return {kBufferWidth, kBufferHeight};
+ }
+ }();
+
+ const auto kUsageFlags = static_cast<uint64_t>(
+ GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+ static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
+ 1u, kUsageFlags, "HdrSdrRatioOverlay");
+
+ const status_t bufferStatus = buffer->initCheck();
+ LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
+ bufferStatus);
+
+ sk_sp<SkSurface> surface =
+ SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
+ SkCanvas* canvas = surface->getCanvas();
+ canvas->setMatrix(canvasTransform);
+
+ drawNumber(currentHdrSdrRatio, 0, color, *canvas);
+
+ void* pixels = nullptr;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+
+ const SkImageInfo& imageInfo = surface->imageInfo();
+ const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
+
+ canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
+ buffer->unlock();
+ return buffer;
+}
+
+HdrSdrRatioOverlay::HdrSdrRatioOverlay()
+ : mSurfaceControl(
+ SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
+ if (!mSurfaceControl) {
+ ALOGE("%s: Failed to create buffer state layer", __func__);
+ return;
+ }
+ SurfaceComposerClient::Transaction()
+ .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl->get(), true)
+ .apply();
+}
+
+void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
+ mCurrentHdrSdrRatio = currentHdrSdrRatio;
+ animate();
+}
+
+void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
+ SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+}
+
+void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
+ constexpr int32_t kMaxWidth = 1000;
+ const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
+ const auto height = 2 * width;
+ Rect frame((5 * width) >> 4, height >> 5);
+ // set the ratio frame to the top right of the screen
+ frame.offsetBy(viewport.width - frame.width(), height >> 4);
+
+ SurfaceComposerClient::Transaction()
+ .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+ 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl->get(), frame.left, frame.top)
+ .apply();
+}
+
+auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> {
+ static const sp<GraphicBuffer> kNoBuffer;
+ if (!mSurfaceControl) return kNoBuffer;
+
+ const auto transformHint =
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
+
+ // Tell SurfaceFlinger about the pre-rotation on the buffer.
+ const auto transform = [&] {
+ switch (transformHint) {
+ case ui::Transform::ROT_90:
+ return ui::Transform::ROT_270;
+ case ui::Transform::ROT_270:
+ return ui::Transform::ROT_90;
+ default:
+ return ui::Transform::ROT_0;
+ }
+ }();
+
+ SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+
+ constexpr SkColor kMinRatioColor = SK_ColorBLUE;
+ constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
+ constexpr float kAlpha = 0.8f;
+
+ // 9.f is picked here as ratio range, given that we assume that
+ // hdr/sdr ratio is [1.f, 10.f)
+ const float scale = currentHdrSdrRatio / 9.f;
+
+ SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale;
+ const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale);
+
+ colorBase.fR = colorBase.fR + minRatioColor.fR;
+ colorBase.fG = colorBase.fG + minRatioColor.fG;
+ colorBase.fB = colorBase.fB + minRatioColor.fB;
+ colorBase.fA = kAlpha;
+
+ const SkColor color = colorBase.toSkColor();
+
+ auto buffer = draw(currentHdrSdrRatio, color, transformHint);
+ return buffer;
+}
+
+void HdrSdrRatioOverlay::animate() {
+ if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
+
+ SurfaceComposerClient::Transaction()
+ .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
+ .apply();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
new file mode 100644
index 0000000..8a2586e
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Utils/OverlayUtils.h"
+
+#include <ui/Size.h>
+#include <utils/StrongPointer.h>
+
+class SkCanvas;
+
+namespace android {
+class HdrSdrRatioOverlay {
+public:
+ HdrSdrRatioOverlay();
+ void setLayerStack(ui::LayerStack);
+ void setViewport(ui::Size);
+ void animate();
+ void changeHdrSdrRatio(float currentRatio);
+
+private:
+ float mCurrentHdrSdrRatio = 1.f;
+
+ static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags);
+ static void drawNumber(float number, int left, SkColor, SkCanvas&);
+
+ const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio);
+
+ const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
+};
+} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5a010e8..3a41f15 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -402,7 +402,7 @@
mLastComputedTrustedPresentationState = false;
if (!leaveState) {
- const auto outputLayer = findOutputLayerForDisplay(display);
+ const auto outputLayer = findOutputLayerForDisplay(display, snapshot->path);
if (outputLayer != nullptr) {
if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) {
Region coveredRegion =
@@ -741,6 +741,11 @@
aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
+ return getCompositionType(outputLayer);
+}
+
+aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
+ const compositionengine::OutputLayer* outputLayer) const {
if (outputLayer == nullptr) {
return aidl::android::hardware::graphics::composer3::Composition::INVALID;
}
@@ -1611,7 +1616,7 @@
result.append("\n");
}
-void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
+void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
if (!outputLayer) {
return;
@@ -1662,6 +1667,41 @@
result.append("\n");
}
+void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot,
+ const DisplayDevice& display) const {
+ const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path);
+ if (!outputLayer) {
+ return;
+ }
+
+ StringAppendF(&result, " %s\n", snapshot.debugName.c_str());
+ StringAppendF(&result, " %10zu | ", snapshot.globalZ);
+ StringAppendF(&result, " %10d | ",
+ snapshot.layerMetadata.getInt32(gui::METADATA_WINDOW_TYPE, 0));
+ StringAppendF(&result, "%10s | ", toString(getCompositionType(outputLayer)).c_str());
+ const auto& outputLayerState = outputLayer->getState();
+ StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
+ const Rect& frame = outputLayerState.displayFrame;
+ StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
+ const FloatRect& crop = outputLayerState.sourceCrop;
+ StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
+ crop.bottom);
+ const auto frameRate = snapshot.frameRate;
+ if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
+ StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
+ ftl::enum_string(frameRate.type).c_str(),
+ ftl::enum_string(frameRate.seamlessness).c_str());
+ } else {
+ result.append(41, ' ');
+ }
+
+ const auto focused = isLayerFocusedBasedOnPriority(snapshot.frameRateSelectionPriority);
+ StringAppendF(&result, " [%s]\n", focused ? "*" : " ");
+
+ result.append(kDumpTableRowLength, '-');
+ result.append("\n");
+}
+
void Layer::dumpFrameStats(std::string& result) const {
mFrameTracker.dumpStats(result);
}
@@ -2578,6 +2618,24 @@
return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
}
+compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
+ const DisplayDevice* display, const frontend::LayerHierarchy::TraversalPath& path) const {
+ if (!display) return nullptr;
+ if (!mFlinger->mLayerLifecycleManagerEnabled) {
+ return display->getCompositionDisplay()->getOutputLayerForLayer(
+ getCompositionEngineLayerFE());
+ }
+ sp<LayerFE> layerFE;
+ for (auto& [p, layer] : mLayerFEs) {
+ if (p == path) {
+ layerFE = layer;
+ }
+ }
+
+ if (!layerFE) return nullptr;
+ return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
+}
+
Region Layer::getVisibleRegion(const DisplayDevice* display) const {
const auto outputLayer = findOutputLayerForDisplay(display);
return outputLayer ? outputLayer->getState().visibleRegion : Region();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2fbbbdc..5d77657 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -343,6 +343,7 @@
virtual sp<LayerFE> getCompositionEngineLayerFE() const;
virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
+ sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
const frontend::LayerSnapshot* getLayerSnapshot() const;
frontend::LayerSnapshot* editLayerSnapshot();
@@ -692,7 +693,8 @@
gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
- void miniDump(std::string& result, const DisplayDevice&) const;
+ void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
+ void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
@@ -960,6 +962,8 @@
void addZOrderRelative(const wp<Layer>& relative);
void removeZOrderRelative(const wp<Layer>& relative);
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+ compositionengine::OutputLayer* findOutputLayerForDisplay(
+ const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const;
bool usingRelativeZ(LayerVector::StateSet) const;
virtual ui::Transform getInputTransform() const;
@@ -1064,7 +1068,8 @@
aidl::android::hardware::graphics::composer3::Composition getCompositionType(
const DisplayDevice&) const;
-
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType(
+ const compositionengine::OutputLayer*) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
* That includes the current layer and all its descendants.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f1fd6db..577211f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,104 +16,20 @@
#include <algorithm>
-#include "BackgroundExecutor.h"
#include "Client.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <SkCanvas.h>
-#include <SkPaint.h>
-#pragma clang diagnostic pop
-#include <SkBlendMode.h>
-#include <SkRect.h>
#include <SkSurface.h>
-#include <gui/SurfaceControl.h>
#undef LOG_TAG
#define LOG_TAG "RefreshRateOverlay"
namespace android {
-namespace {
-constexpr int kDigitWidth = 64;
-constexpr int kDigitHeight = 100;
-constexpr int kDigitSpace = 16;
-
-constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
-constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
-constexpr int kBufferHeight = kDigitHeight;
-
-} // namespace
-
-SurfaceControlHolder::~SurfaceControlHolder() {
- // Hand the sp<SurfaceControl> to the helper thread to release the last
- // reference. This makes sure that the SurfaceControl is destructed without
- // SurfaceFlinger::mStateLock held.
- BackgroundExecutor::getInstance().sendCallbacks(
- {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
- SkCanvas& canvas) {
- const SkRect rect = [&]() {
- switch (segment) {
- case Segment::Upper:
- return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
- case Segment::UpperLeft:
- return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2);
- case Segment::UpperRight:
- return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
- kDigitHeight / 2);
- case Segment::Middle:
- return SkRect::MakeLTRB(left, kDigitHeight / 2 - kDigitSpace / 2,
- left + kDigitWidth, kDigitHeight / 2 + kDigitSpace / 2);
- case Segment::LowerLeft:
- return SkRect::MakeLTRB(left, kDigitHeight / 2, left + kDigitSpace, kDigitHeight);
- case Segment::LowerRight:
- return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2,
- left + kDigitWidth, kDigitHeight);
- case Segment::Bottom:
- return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
- kDigitHeight);
- }
- }();
-
- SkPaint paint;
- paint.setColor(color);
- paint.setBlendMode(SkBlendMode::kSrc);
- canvas.drawRect(rect, paint);
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor color,
- SkCanvas& canvas) {
- if (digit < 0 || digit > 9) return;
-
- if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
- digit == 8 || digit == 9)
- drawSegment(Segment::Upper, left, color, canvas);
- if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
- drawSegment(Segment::UpperLeft, left, color, canvas);
- if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
- digit == 8 || digit == 9)
- drawSegment(Segment::UpperRight, left, color, canvas);
- if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
- digit == 9)
- drawSegment(Segment::Middle, left, color, canvas);
- if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
- drawSegment(Segment::LowerLeft, left, color, canvas);
- if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
- digit == 7 || digit == 8 || digit == 9)
- drawSegment(Segment::LowerRight, left, color, canvas);
- if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
- digit == 9)
- drawSegment(Segment::Bottom, left, color, canvas);
-}
-
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
- ui::Transform::RotationFlags rotation,
- ftl::Flags<Features> features) -> Buffers {
+auto RefreshRateOverlay::draw(int displayFps, int renderFps, SkColor color,
+ ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
+ -> Buffers {
const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
Buffers buffers;
@@ -148,7 +64,8 @@
LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
bufferStatus);
- sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+ sk_sp<SkSurface> surface = SkSurfaces::Raster(
+ SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
SkCanvas* canvas = surface->getCanvas();
canvas->setMatrix(canvasTransform);
@@ -158,22 +75,27 @@
if (features.test(Features::Spinner)) {
switch (i) {
case 0:
- drawSegment(Segment::Upper, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Upper, left, color, *canvas);
break;
case 1:
- drawSegment(Segment::UpperRight, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperRight, left, color,
+ *canvas);
break;
case 2:
- drawSegment(Segment::LowerRight, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerRight, left, color,
+ *canvas);
break;
case 3:
- drawSegment(Segment::Bottom, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Bottom, left, color,
+ *canvas);
break;
case 4:
- drawSegment(Segment::LowerLeft, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerLeft, left, color,
+ *canvas);
break;
case 5:
- drawSegment(Segment::UpperLeft, left, color, *canvas);
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperLeft, left, color,
+ *canvas);
break;
}
}
@@ -199,34 +121,27 @@
return buffers;
}
-void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
- SkCanvas& canvas) {
+void RefreshRateOverlay::drawNumber(int number, int left, SkColor color, SkCanvas& canvas) {
if (number < 0 || number >= 1000) return;
if (number >= 100) {
- drawDigit(number / 100, left, color, canvas);
+ SegmentDrawer::drawDigit(number / 100, left, color, canvas);
}
left += kDigitWidth + kDigitSpace;
if (number >= 10) {
- drawDigit((number / 10) % 10, left, color, canvas);
+ SegmentDrawer::drawDigit((number / 10) % 10, left, color, canvas);
}
left += kDigitWidth + kDigitSpace;
- drawDigit(number % 10, left, color, canvas);
-}
-
-std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
- sp<SurfaceControl> surfaceControl =
- SurfaceComposerClient::getDefault()
- ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState);
- return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+ SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
- : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
+ : mFpsRange(fpsRange),
+ mFeatures(features),
+ mSurfaceControl(
+ SurfaceControlHolder::createSurfaceControlHolder(String8("RefreshRateOverlay"))) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
@@ -295,8 +210,7 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
- mFeatures);
+ auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures);
it = mBufferCache
.try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
.first;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 0b89b8e..65c61cb 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,12 +16,12 @@
#pragma once
-#include <SkColor.h>
+#include "Utils/OverlayUtils.h"
+
#include <vector>
#include <ftl/flags.h>
#include <ftl/small_map.h>
-#include <gui/SurfaceComposerClient.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
#include <ui/Transform.h>
@@ -34,22 +34,8 @@
namespace android {
class GraphicBuffer;
-class SurfaceControl;
class SurfaceFlinger;
-// Helper class to delete the SurfaceControl on a helper thread as
-// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
-class SurfaceControlHolder {
-public:
- explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
- ~SurfaceControlHolder();
-
- const sp<SurfaceControl>& get() const { return mSurfaceControl; }
-
-private:
- sp<SurfaceControl> mSurfaceControl;
-};
-
class RefreshRateOverlay {
public:
enum class Features {
@@ -70,18 +56,9 @@
private:
using Buffers = std::vector<sp<GraphicBuffer>>;
- class SevenSegmentDrawer {
- public:
- static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
- ftl::Flags<Features>);
-
- private:
- enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
-
- static void drawSegment(Segment, int left, SkColor, SkCanvas&);
- static void drawDigit(int digit, int left, SkColor, SkCanvas&);
- static void drawNumber(int number, int left, SkColor, SkCanvas&);
- };
+ static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+ ftl::Flags<Features>);
+ static void drawNumber(int number, int left, SkColor, SkCanvas&);
const Buffers& getOrCreateBuffers(Fps, Fps);
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 281b0ae..c70ed2c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -427,11 +427,6 @@
mCondition.notify_all();
}
-size_t EventThread::getEventThreadConnectionCount() {
- std::lock_guard<std::mutex> lock(mMutex);
- return mDisplayEventConnections.size();
-}
-
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 684745b..7023445 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -131,9 +131,6 @@
virtual VsyncEventData getLatestVsyncEventData(
const sp<EventThreadConnection>& connection) const = 0;
- // Retrieves the number of event connections tracked by this EventThread.
- virtual size_t getEventThreadConnectionCount() = 0;
-
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
};
@@ -172,8 +169,6 @@
void setDuration(std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) override;
- size_t getEventThreadConnectionCount() override;
-
void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
private:
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index badbf53..3b61de7 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -29,6 +29,7 @@
virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
+ virtual void onChoreographerAttached() = 0;
protected:
~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index beaf972..5d00a26 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -131,22 +131,6 @@
const auto& info = layerPair->second;
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
- // Set frame rate to attached choreographer.
- // TODO(b/260898223): Change to use layer hierarchy and handle frame rate vote.
- if (updateType == LayerUpdateType::SetFrameRate) {
- auto range = mAttachedChoreographers.equal_range(id);
- auto it = range.first;
- while (it != range.second) {
- sp<EventThreadConnection> choreographerConnection = it->second.promote();
- if (choreographerConnection) {
- choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
- it++;
- } else {
- it = mAttachedChoreographers.erase(it);
- }
- }
- }
-
// Activate layer if inactive.
if (found == LayerStatus::LayerInInactiveMap) {
mActiveLayerInfos.insert(
@@ -301,12 +285,6 @@
return 0.f;
}
-void LayerHistory::attachChoreographer(int32_t layerId,
- const sp<EventThreadConnection>& choreographerConnection) {
- std::lock_guard lock(mLock);
- mAttachedChoreographers.insert({layerId, wp<EventThreadConnection>(choreographerConnection)});
-}
-
auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> {
// the layer could be in either the active or inactive map, try both
auto it = mActiveLayerInfos.find(id);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 69caf9f..d083fa2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -84,9 +84,6 @@
// return the frames per second of the layer with the given sequence id.
float getLayerFramerate(nsecs_t now, int32_t id) const;
- void attachChoreographer(int32_t layerId,
- const sp<EventThreadConnection>& choreographerConnection);
-
private:
friend class LayerHistoryTest;
friend class TestableScheduler;
@@ -124,10 +121,6 @@
LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
- // Map keyed by layer ID (sequence) to choreographer connections.
- std::unordered_multimap<int32_t, wp<EventThreadConnection>> mAttachedChoreographers
- GUARDED_BY(mLock);
-
uint32_t mDisplayArea = 0;
// Whether to emit systrace output and debug logs.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index c44e22e..fb985f7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -866,6 +866,8 @@
return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
});
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
+ ATRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
+ to_string(overrideFps).c_str(), uid);
frameRateOverrides.emplace(uid, overrideFps);
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0dca21e..d6d7725 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -283,29 +283,40 @@
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
- auto connection = createConnectionInternal(eventThread.get());
+ auto connection = eventThread->createEventConnection([&] { resync(); });
std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
return handle;
}
-sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, EventRegistrationFlags eventRegistration,
- const sp<IBinder>& layerHandle) {
- int32_t layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
- auto connection = eventThread->createEventConnection([&] { resync(); }, eventRegistration);
- mLayerHistory.attachChoreographer(layerId, connection);
- return connection;
-}
-
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
ConnectionHandle handle, EventRegistrationFlags eventRegistration,
const sp<IBinder>& layerHandle) {
- std::lock_guard<std::mutex> lock(mConnectionsLock);
- RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration,
- layerHandle);
+ const auto connection = [&]() -> sp<EventThreadConnection> {
+ std::scoped_lock lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle, nullptr);
+
+ return mConnections[handle].thread->createEventConnection([&] { resync(); },
+ eventRegistration);
+ }();
+ const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
+
+ if (layerId != static_cast<int32_t>(UNASSIGNED_LAYER_ID)) {
+ // TODO(b/290409668): Moving the choreographer attachment to be a transaction that will be
+ // processed on the main thread.
+ mSchedulerCallback.onChoreographerAttached();
+
+ std::scoped_lock lock(mChoreographerLock);
+ const auto [iter, emplaced] =
+ mAttachedChoreographers.emplace(layerId,
+ AttachedChoreographers{Fps(), {connection}});
+ if (!emplaced) {
+ iter->second.connections.emplace(connection);
+ connection->frameRate = iter->second.frameRate;
+ }
+ }
+ return connection;
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -405,12 +416,6 @@
thread->onModeChanged(mode);
}
-size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
- std::lock_guard<std::mutex> lock(mConnectionsLock);
- RETURN_IF_INVALID_HANDLE(handle, 0);
- return mConnections[handle].thread->getEventThreadConnectionCount();
-}
-
void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
android::EventThread* thread;
{
@@ -586,6 +591,11 @@
mLayerHistory.deregisterLayer(layer);
}
+void Scheduler::onLayerDestroyed(Layer* layer) {
+ std::scoped_lock lock(mChoreographerLock);
+ mAttachedChoreographers.erase(layer->getSequence());
+}
+
void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
if (pacesetterSelectorPtr()->canSwitch()) {
@@ -602,7 +612,9 @@
mFeatures.test(Feature::kContentDetection));
}
-void Scheduler::chooseRefreshRateForContent() {
+void Scheduler::chooseRefreshRateForContent(
+ const surfaceflinger::frontend::LayerHierarchy* hierarchy,
+ bool updateAttachedChoreographer) {
const auto selectorPtr = pacesetterSelectorPtr();
if (!selectorPtr->canSwitch()) return;
@@ -610,6 +622,20 @@
LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
applyPolicy(&Policy::contentRequirements, std::move(summary));
+
+ if (updateAttachedChoreographer) {
+ LOG_ALWAYS_FATAL_IF(!hierarchy);
+
+ // update the attached choreographers after we selected the render rate.
+ const ftl::Optional<FrameRateMode> modeOpt = [&] {
+ std::scoped_lock lock(mPolicyLock);
+ return mPolicy.modeOpt;
+ }();
+
+ if (modeOpt) {
+ updateAttachedChoreographers(*hierarchy, modeOpt->fps);
+ }
+ }
}
void Scheduler::resetIdleTimer() {
@@ -792,6 +818,12 @@
}
bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ std::scoped_lock lock(mPolicyLock);
+ return updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate);
+}
+
+bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
+ Fps displayRefreshRate) {
if (consideredSignals.idle) return false;
const auto frameRateOverrides =
@@ -869,6 +901,105 @@
mPolicy = {};
}
+void Scheduler::updateAttachedChoreographersFrameRate(
+ const surfaceflinger::frontend::RequestedLayerState& layer, Fps fps) {
+ std::scoped_lock lock(mChoreographerLock);
+
+ const auto layerId = static_cast<int32_t>(layer.id);
+ const auto choreographers = mAttachedChoreographers.find(layerId);
+ if (choreographers == mAttachedChoreographers.end()) {
+ return;
+ }
+
+ auto& layerChoreographers = choreographers->second;
+
+ layerChoreographers.frameRate = fps;
+ ATRACE_FORMAT_INSTANT("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+ ALOGV("%s: %s for %s", __func__, to_string(fps).c_str(), layer.name.c_str());
+
+ auto it = layerChoreographers.connections.begin();
+ while (it != layerChoreographers.connections.end()) {
+ sp<EventThreadConnection> choreographerConnection = it->promote();
+ if (choreographerConnection) {
+ choreographerConnection->frameRate = fps;
+ it++;
+ } else {
+ it = choreographers->second.connections.erase(it);
+ }
+ }
+
+ if (layerChoreographers.connections.empty()) {
+ mAttachedChoreographers.erase(choreographers);
+ }
+}
+
+int Scheduler::updateAttachedChoreographersInternal(
+ const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate,
+ int parentDivisor) {
+ const char* name = layerHierarchy.getLayer() ? layerHierarchy.getLayer()->name.c_str() : "Root";
+
+ int divisor = 0;
+ if (layerHierarchy.getLayer()) {
+ const auto frameRateCompatibility = layerHierarchy.getLayer()->frameRateCompatibility;
+ const auto frameRate = Fps::fromValue(layerHierarchy.getLayer()->frameRate);
+ ALOGV("%s: %s frameRate %s parentDivisor=%d", __func__, name, to_string(frameRate).c_str(),
+ parentDivisor);
+
+ if (frameRate.isValid()) {
+ if (frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ||
+ frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_EXACT) {
+ // Since this layer wants an exact match, we would only set a frame rate if the
+ // desired rate is a divisor of the display refresh rate.
+ divisor = RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate);
+ } else if (frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) {
+ // find the closest frame rate divisor for the desired frame rate.
+ divisor = static_cast<int>(
+ std::round(displayRefreshRate.getValue() / frameRate.getValue()));
+ }
+ }
+ }
+
+ // We start by traversing the children, updating their choreographers, and getting back the
+ // aggregated frame rate.
+ int childrenDivisor = 0;
+ for (const auto& [child, _] : layerHierarchy.mChildren) {
+ LOG_ALWAYS_FATAL_IF(child == nullptr || child->getLayer() == nullptr);
+
+ ALOGV("%s: %s traversing child %s", __func__, name, child->getLayer()->name.c_str());
+
+ const int childDivisor =
+ updateAttachedChoreographersInternal(*child, displayRefreshRate, divisor);
+ childrenDivisor = childrenDivisor > 0 ? childrenDivisor : childDivisor;
+ if (childDivisor > 0) {
+ childrenDivisor = std::gcd(childrenDivisor, childDivisor);
+ }
+ ALOGV("%s: %s childrenDivisor=%d", __func__, name, childrenDivisor);
+ }
+
+ ALOGV("%s: %s divisor=%d", __func__, name, divisor);
+
+ // If there is no explicit vote for this layer. Use the children's vote if exists
+ divisor = (divisor == 0) ? childrenDivisor : divisor;
+ ALOGV("%s: %s divisor=%d with children", __func__, name, divisor);
+
+ // If there is no explicit vote for this layer or its children, Use the parent vote if exists
+ divisor = (divisor == 0) ? parentDivisor : divisor;
+ ALOGV("%s: %s divisor=%d with parent", __func__, name, divisor);
+
+ if (layerHierarchy.getLayer()) {
+ Fps fps = divisor > 1 ? displayRefreshRate / (unsigned int)divisor : Fps();
+ updateAttachedChoreographersFrameRate(*layerHierarchy.getLayer(), fps);
+ }
+
+ return divisor;
+}
+
+void Scheduler::updateAttachedChoreographers(
+ const surfaceflinger::frontend::LayerHierarchy& layerHierarchy, Fps displayRefreshRate) {
+ ATRACE_CALL();
+ updateAttachedChoreographersInternal(layerHierarchy, displayRefreshRate, 0);
+}
+
template <typename S, typename T>
auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
ATRACE_CALL();
@@ -911,7 +1042,7 @@
.emitEvent = !choice.consideredSignals.idle});
}
- frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps);
+ frameRateOverridesChanged = updateFrameRateOverridesLocked(consideredSignals, modeOpt->fps);
if (mPolicy.modeOpt != modeOpt) {
mPolicy.modeOpt = modeOpt;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index b913700..85d0f9a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -52,6 +52,8 @@
#include "Utils/Dumper.h"
#include "VsyncModulator.h"
+#include <FrontEnd/LayerHierarchy.h>
+
namespace android::scheduler {
// Opaque handle to scheduler connection.
@@ -153,7 +155,7 @@
sp<IDisplayEventConnection> createDisplayEventConnection(
ConnectionHandle, EventRegistrationFlags eventRegistration = {},
- const sp<IBinder>& layerHandle = nullptr);
+ const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock);
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
@@ -230,9 +232,11 @@
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
+ void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
// Detects content using layer history, and selects a matching refresh rate.
- void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
+ void chooseRefreshRateForContent(const surfaceflinger::frontend::LayerHierarchy*,
+ bool updateAttachedChoreographer) EXCLUDES(mDisplayLock);
void resetIdleTimer();
@@ -281,8 +285,6 @@
// Notifies the scheduler when the display size has changed. Called from SF's main thread
void onActiveDisplayAreaChanged(uint32_t displayArea);
- size_t getEventThreadConnectionCount(ConnectionHandle handle);
-
// Stores the preferred refresh rate that an app should run at.
// FrameRateOverride.refreshRateHz == 0 means no preference.
void setPreferredRefreshRateForUid(FrameRateOverride);
@@ -305,6 +307,8 @@
return mLayerHistory.getLayerFramerate(now, id);
}
+ bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
+
private:
friend class TestableScheduler;
@@ -318,9 +322,6 @@
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
- sp<EventThreadConnection> createConnectionInternal(
- EventThread*, EventRegistrationFlags eventRegistration = {},
- const sp<IBinder>& layerHandle = nullptr);
// Update feature state machine to given state when corresponding timer resets or expires.
void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
@@ -393,7 +394,14 @@
GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
- bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
+ bool updateFrameRateOverridesLocked(GlobalSignals, Fps displayRefreshRate)
+ REQUIRES(mPolicyLock);
+ void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&,
+ Fps displayRefreshRate);
+ int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&,
+ Fps displayRefreshRate, int parentDivisor);
+ void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&,
+ Fps fps) EXCLUDES(mChoreographerLock);
void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
@@ -523,6 +531,16 @@
std::optional<ModeChangedParams> cachedModeChangedParams;
} mPolicy GUARDED_BY(mPolicyLock);
+ std::mutex mChoreographerLock;
+
+ struct AttachedChoreographers {
+ Fps frameRate;
+ std::unordered_set<wp<EventThreadConnection>, WpHash> connections;
+ };
+ // Map keyed by layer ID (sequence) to choreographer connections.
+ std::unordered_map<int32_t, AttachedChoreographers> mAttachedChoreographers
+ GUARDED_BY(mChoreographerLock);
+
std::mutex mVsyncTimelineLock;
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 6499d69..e0fb8f9 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -146,13 +146,14 @@
void cancelTimer() REQUIRES(mMutex);
ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
+ std::mutex mutable mMutex;
+
static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
std::unique_ptr<TimeKeeper> const mTimeKeeper;
VsyncSchedule::TrackerPtr mTracker;
nsecs_t const mTimerSlack;
nsecs_t const mMinVsyncDistance;
- std::mutex mutable mMutex;
size_t mCallbackToken GUARDED_BY(mMutex) = 0;
CallbackMap mCallbacks GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 0757b57..47e92e1 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -21,6 +21,7 @@
#include <string>
#include <android-base/thread_annotations.h>
+#include <ThreadContext.h>
#include <ftl/enum.h>
#include <ftl/optional.h>
#include <ui/DisplayId.h>
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index de0034b..9f24dd6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -24,6 +24,7 @@
#include "SurfaceFlinger.h"
+#include <aidl/android/hardware/power/Boost.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -34,7 +35,6 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/Boost.h>
#include <android/native_window.h>
#include <android/os/IInputFlinger.h>
#include <binder/IPCThreadState.h>
@@ -68,6 +68,7 @@
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
@@ -91,13 +92,12 @@
#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
+#include <unistd.h>
#include <utils/StopWatch.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/misc.h>
-
-#include <unistd.h>
#include <algorithm>
#include <cerrno>
#include <cinttypes>
@@ -326,6 +326,7 @@
bool SurfaceFlinger::useHwcForRgbToYuv;
bool SurfaceFlinger::hasSyncFramework;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+int64_t SurfaceFlinger::minAcquiredBuffers = 1;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::useContextPriority;
@@ -377,6 +378,7 @@
}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
+ ATRACE_CALL();
ALOGI("SurfaceFlinger is starting");
hasSyncFramework = running_without_sync_framework(true);
@@ -386,6 +388,8 @@
useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
+ minAcquiredBuffers =
+ SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
@@ -402,9 +406,6 @@
wideColorGamutCompositionPixelFormat =
static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
- mColorSpaceAgnosticDataspace =
- static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
-
mLayerCachingEnabled = [] {
const bool enable =
android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
@@ -493,10 +494,6 @@
return LatchUnsignaledConfig::AutoSingleLayer;
}
- if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
- return LatchUnsignaledConfig::Always;
- }
-
return LatchUnsignaledConfig::Disabled;
}
@@ -690,7 +687,7 @@
// wait patiently for the window manager death
const String16 name("window");
- mWindowManager = defaultServiceManager()->getService(name);
+ mWindowManager = defaultServiceManager()->waitForService(name);
if (mWindowManager != 0) {
mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this));
}
@@ -704,7 +701,7 @@
LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
- sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
+ sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
if (input == nullptr) {
@@ -803,6 +800,7 @@
// Do not call property_set on main thread which will be blocked by init
// Use StartPropertySetThread instead.
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
+ ATRACE_CALL();
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
addTransactionReadyFilters();
@@ -1525,7 +1523,7 @@
}
display->getCompositionDisplay()->setColorProfile(
- {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+ {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC});
return NO_ERROR;
});
@@ -1945,10 +1943,28 @@
float currentDimmingRatio =
compositionDisplay->editState().sdrWhitePointNits /
compositionDisplay->editState().displayBrightnessNits;
- compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
- brightness.displayBrightnessNits);
+ static constexpr float kDimmingThreshold = 0.02f;
+ if (brightness.sdrWhitePointNits == 0.f ||
+ abs(brightness.sdrWhitePointNits - brightness.displayBrightnessNits) /
+ brightness.sdrWhitePointNits >=
+ kDimmingThreshold) {
+ // to optimize, skip brightness setter if the brightness difference ratio
+ // is lower than threshold
+ compositionDisplay
+ ->setDisplayBrightness(brightness.sdrWhitePointNits,
+ brightness.displayBrightnessNits);
+ } else {
+ compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
+ brightness.sdrWhitePointNits);
+ }
+
FTL_FAKE_GUARD(kMainThreadContext,
display->stageBrightness(brightness.displayBrightness));
+ float currentHdrSdrRatio =
+ compositionDisplay->editState().displayBrightnessNits /
+ compositionDisplay->editState().sdrWhitePointNits;
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->updateHdrSdrRatioOverlayRatio(currentHdrSdrRatio));
if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits !=
currentDimmingRatio) {
@@ -2020,7 +2036,7 @@
}
status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
- using hardware::power::Boost;
+ using aidl::android::hardware::power::Boost;
Boost powerBoost = static_cast<Boost>(boostId);
if (powerBoost == Boost::INTERACTION) {
@@ -2169,12 +2185,22 @@
}
}
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
- bool transactionsFlushed,
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs,
+ bool flushTransactions,
bool& outTransactionsAreEmpty) {
ATRACE_CALL();
+ frontend::Update update;
+ if (flushTransactions) {
+ update = flushLifecycleUpdates();
+ if (mTransactionTracing) {
+ mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
+ update, mFrontEndDisplayInfos,
+ mFrontEndDisplayInfosChanged);
+ }
+ }
+
bool needsTraversal = false;
- if (transactionsFlushed) {
+ if (flushTransactions) {
needsTraversal |= commitMirrorDisplays(vsyncId);
needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
needsTraversal |= applyTransactions(update.transactions, vsyncId);
@@ -2222,12 +2248,43 @@
}
}
-bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
- bool transactionsFlushed, bool& outTransactionsAreEmpty) {
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
+ bool flushTransactions, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
ATRACE_CALL();
- {
+ frontend::Update update;
+ if (flushTransactions) {
+ ATRACE_NAME("TransactionHandler:flushTransactions");
+ // Locking:
+ // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+ // we must keep a copy of the transactions (specifically the composer
+ // states) around outside the scope of the lock.
+ // 2. Transactions and created layers do not share a lock. To prevent applying
+ // transactions with layers still in the createdLayer queue, collect the transactions
+ // before committing the created layers.
+ // 3. Transactions can only be flushed after adding layers, since the layer can be a newly
+ // created one
+ mTransactionHandler.collectTransactions();
+ {
+ // TODO(b/238781169) lockless queue this and keep order.
+ std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+ update.layerCreatedStates = std::move(mCreatedLayers);
+ mCreatedLayers.clear();
+ update.newLayers = std::move(mNewLayers);
+ mNewLayers.clear();
+ update.layerCreationArgs = std::move(mNewLayerArgs);
+ mNewLayerArgs.clear();
+ update.destroyedHandles = std::move(mDestroyedHandles);
+ mDestroyedHandles.clear();
+ }
+
mLayerLifecycleManager.addLayers(std::move(update.newLayers));
+ update.transactions = mTransactionHandler.flushTransactions();
+ if (mTransactionTracing) {
+ mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
+ update, mFrontEndDisplayInfos,
+ mFrontEndDisplayInfosChanged);
+ }
mLayerLifecycleManager.applyTransactions(update.transactions);
mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
for (auto& legacyLayer : update.layerCreatedStates) {
@@ -2236,11 +2293,11 @@
mLegacyLayers[layer->sequence] = layer;
}
}
- }
- if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
- ATRACE_NAME("LayerHierarchyBuilder:update");
- mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
- mLayerLifecycleManager.getDestroyedLayers());
+ if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
+ ATRACE_NAME("LayerHierarchyBuilder:update");
+ mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
+ mLayerLifecycleManager.getDestroyedLayers());
+ }
}
bool mustComposite = false;
@@ -2270,6 +2327,11 @@
Changes::Visibility)) {
mVisibleRegionsDirty = true;
}
+ if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
+ // The frame rate of attached choreographers can only change as a result of a
+ // FrameRate change (including when Hierarchy changes).
+ mUpdateAttachedChoreographer = true;
+ }
outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
@@ -2398,10 +2460,10 @@
mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
}
- if (mRefreshRateOverlaySpinner) {
+ if (mRefreshRateOverlaySpinner || mHdrSdrRatioOverlay) {
Mutex::Autolock lock(mStateLock);
if (const auto display = getDefaultDisplayDeviceLocked()) {
- display->animateRefreshRateOverlay();
+ display->animateOverlay();
}
}
@@ -2413,25 +2475,16 @@
Fps::fromPeriodNsecs(vsyncPeriod.ns()));
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
- frontend::Update updates;
- if (flushTransactions) {
- updates = flushLifecycleUpdates();
- if (mTransactionTracing) {
- mTransactionTracing
- ->addCommittedTransactions(ftl::to_underlying(vsyncId),
- pacesetterFrameTarget.frameBeginTime().ns(),
- updates, mFrontEndDisplayInfos,
- mFrontEndDisplayInfosChanged);
- }
- }
bool transactionsAreEmpty;
if (mLegacyFrontEndEnabled) {
- mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
- transactionsAreEmpty);
+ mustComposite |=
+ updateLayerSnapshotsLegacy(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+ flushTransactions, transactionsAreEmpty);
}
if (mLayerLifecycleManagerEnabled) {
mustComposite |=
- updateLayerSnapshots(vsyncId, updates, flushTransactions, transactionsAreEmpty);
+ updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
+ flushTransactions, transactionsAreEmpty);
}
if (transactionFlushNeeded()) {
@@ -2454,13 +2507,21 @@
// Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
// and may eventually call to ~Layer() if it holds the last reference
{
+ bool updateAttachedChoreographer = mUpdateAttachedChoreographer;
+ mUpdateAttachedChoreographer = false;
+
Mutex::Autolock lock(mStateLock);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled
+ ? &mLayerHierarchyBuilder.getHierarchy()
+ : nullptr,
+ updateAttachedChoreographer);
setActiveModeInHwcIfNeeded();
}
updateCursorAsync();
- updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+ if (!mustComposite) {
+ updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
+ }
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
@@ -2545,7 +2606,6 @@
refreshArgs.outputColorSetting = useColorManagement
? mDisplayColorSetting
: compositionengine::OutputColorSetting::kUnmanaged;
- refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
@@ -2672,6 +2732,8 @@
addToLayerTracing(mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId);
}
+ updateInputFlinger(vsyncId, pacesetterTarget.frameBeginTime());
+
if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
mVisibleRegionsDirty = false;
@@ -2905,31 +2967,55 @@
for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
HdrLayerInfoReporter::HdrLayerInfo info;
int32_t maxArea = 0;
- mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
- const auto layerFe = layer->getCompositionEngineLayerFE();
- const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
- if (snapshot.isVisible &&
- compositionDisplay->includesLayer(snapshot.outputFilter)) {
- if (isHdrLayer(snapshot)) {
- const auto* outputLayer =
- compositionDisplay->getOutputLayerForLayer(layerFe);
- if (outputLayer) {
- const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f
- ? std::numeric_limits<float>::infinity()
- : snapshot.desiredHdrSdrRatio;
- info.mergeDesiredRatio(desiredHdrSdrRatio);
- info.numberOfHdrLayers++;
- const auto displayFrame = outputLayer->getState().displayFrame;
- const int32_t area = displayFrame.width() * displayFrame.height();
- if (area > maxArea) {
- maxArea = area;
- info.maxW = displayFrame.width();
- info.maxH = displayFrame.height();
+ auto updateInfoFn =
+ [&](const std::shared_ptr<compositionengine::Display>& compositionDisplay,
+ const frontend::LayerSnapshot& snapshot, const sp<LayerFE>& layerFe) {
+ if (snapshot.isVisible &&
+ compositionDisplay->includesLayer(snapshot.outputFilter)) {
+ if (isHdrLayer(snapshot)) {
+ const auto* outputLayer =
+ compositionDisplay->getOutputLayerForLayer(layerFe);
+ if (outputLayer) {
+ const float desiredHdrSdrRatio =
+ snapshot.desiredHdrSdrRatio <= 1.f
+ ? std::numeric_limits<float>::infinity()
+ : snapshot.desiredHdrSdrRatio;
+ info.mergeDesiredRatio(desiredHdrSdrRatio);
+ info.numberOfHdrLayers++;
+ const auto displayFrame = outputLayer->getState().displayFrame;
+ const int32_t area =
+ displayFrame.width() * displayFrame.height();
+ if (area > maxArea) {
+ maxArea = area;
+ info.maxW = displayFrame.width();
+ info.maxH = displayFrame.height();
+ }
+ }
}
}
- }
- }
- });
+ };
+
+ if (mLayerLifecycleManagerEnabled) {
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&, compositionDisplay = compositionDisplay](
+ std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ auto& legacyLayer = it->second;
+ sp<LayerFE> layerFe =
+ legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+
+ updateInfoFn(compositionDisplay, *snapshot, layerFe);
+ });
+ } else {
+ mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
+ const auto layerFe = layer->getCompositionEngineLayerFE();
+ const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
+ updateInfoFn(compositionDisplay, snapshot, layerFe);
+ });
+ }
listener->dispatchHdrLayerInfo(info);
}
}
@@ -2961,10 +3047,6 @@
}
}
- const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
- const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
- mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
-
if (hasPacesetterDisplay && !pacesetterDisplay->isPoweredOn()) {
getRenderEngine().cleanupPostRender();
return;
@@ -3080,7 +3162,9 @@
int attempt = 0;
constexpr int kMaxAttempts = 3;
do {
- hwcModes = getHwComposer().getModes(displayId);
+ hwcModes = getHwComposer().getModes(displayId,
+ scheduler::RefreshRateSelector::kMinSupportedFrameRate
+ .getPeriodNsecs());
activeModeHwcId = getHwComposer().getActiveMode(displayId);
const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
@@ -3333,8 +3417,7 @@
}
display->getCompositionDisplay()->setColorProfile(
compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
- RenderIntent::COLORIMETRIC,
- Dataspace::UNKNOWN});
+ RenderIntent::COLORIMETRIC});
if (const auto& physical = state.physical) {
mPhysicalDisplays.get(physical->id)
@@ -3894,6 +3977,14 @@
mPowerAdvisor->notifyCpuLoadUp();
}
+void SurfaceFlinger::onChoreographerAttached() {
+ ATRACE_CALL();
+ if (mLayerLifecycleManagerEnabled) {
+ mUpdateAttachedChoreographer = true;
+ scheduleCommit(FrameHint::kNone);
+ }
+}
+
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
using namespace scheduler;
@@ -4276,18 +4367,16 @@
return TransactionReadiness::Ready;
}
-TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheckLegacy(
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
auto ready = TransactionReadiness::Ready;
- flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s,
- const std::shared_ptr<
- renderengine::
- ExternalTexture>&
- externalTexture)
- -> bool {
- sp<Layer> layer = LayerHandle::getLayer(s.surface);
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
+ resolvedState) -> bool {
+ sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
+
const auto& transaction = *flushState.transaction;
+ const auto& s = resolvedState.state;
// check for barrier frames
if (s.bufferData->hasBarrier) {
// The current producerId is already a newer producer than the buffer that has a
@@ -4295,7 +4384,7 @@
// don't wait on the barrier since we know that's stale information.
if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) {
layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
- externalTexture->getBuffer(),
+ resolvedState.externalTexture->getBuffer(),
s.bufferData->frameNumber,
s.bufferData->acquireFence);
// Delete the entire state at this point and not just release the buffer because
@@ -4332,18 +4421,17 @@
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
- // ignore the acquire fence if LatchUnsignaledConfig::Always is set.
- const bool checkAcquireFence = enableLatchUnsignaledConfig != LatchUnsignaledConfig::Always;
const bool acquireFenceAvailable = s.bufferData &&
s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
s.bufferData->acquireFence;
- const bool fenceSignaled = !checkAcquireFence || !acquireFenceAvailable ||
+ const bool fenceSignaled = !acquireFenceAvailable ||
s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
if (!fenceSignaled) {
// check fence status
- const bool allowLatchUnsignaled =
- shouldLatchUnsignaled(layer, s, transaction.states.size(),
- flushState.firstTransaction);
+ const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
+ flushState.firstTransaction) &&
+ layer->isSimpleBufferUpdate(s);
+
if (allowLatchUnsignaled) {
ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
layer->getDebugName());
@@ -4368,15 +4456,118 @@
return ready;
}
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+ const TransactionHandler::TransactionFlushState& flushState) {
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+ auto ready = TransactionReadiness::Ready;
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
+ resolvedState) -> bool {
+ const frontend::RequestedLayerState* layer =
+ mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
+ const auto& transaction = *flushState.transaction;
+ const auto& s = resolvedState.state;
+ // check for barrier frames
+ if (s.bufferData->hasBarrier) {
+ // The current producerId is already a newer producer than the buffer that has a
+ // barrier. This means the incoming buffer is older and we can release it here. We
+ // don't wait on the barrier since we know that's stale information.
+ if (layer->barrierProducerId > s.bufferData->producerId) {
+ if (s.bufferData->releaseBufferListener) {
+ uint32_t currentMaxAcquiredBufferCount =
+ getMaxAcquiredBufferCountForCurrentRefreshRate(layer->ownerUid.val());
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
+ layer->name.c_str(), s.bufferData->frameNumber);
+ s.bufferData->releaseBufferListener
+ ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()->getId(),
+ s.bufferData->frameNumber},
+ s.bufferData->acquireFence
+ ? s.bufferData->acquireFence
+ : Fence::NO_FENCE,
+ currentMaxAcquiredBufferCount);
+ }
+
+ // Delete the entire state at this point and not just release the buffer because
+ // everything associated with the Layer in this Transaction is now out of date.
+ ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", layer->name.c_str(),
+ layer->barrierProducerId, s.bufferData->producerId);
+ return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
+ }
+
+ if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
+ const bool willApplyBarrierFrame =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+ ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+ s.bufferData->barrierFrameNumber));
+ if (!willApplyBarrierFrame) {
+ ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
+ layer->name.c_str(), layer->barrierFrameNumber,
+ s.bufferData->barrierFrameNumber);
+ ready = TransactionReadiness::NotReadyBarrier;
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+ }
+ }
+
+ // If backpressure is enabled and we already have a buffer to commit, keep
+ // the transaction in the queue.
+ const bool hasPendingBuffer =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+ if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+ ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
+ ready = TransactionReadiness::NotReady;
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+
+ const bool acquireFenceAvailable = s.bufferData &&
+ s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ s.bufferData->acquireFence;
+ const bool fenceSignaled = !acquireFenceAvailable ||
+ s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
+ if (!fenceSignaled) {
+ // check fence status
+ const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
+ flushState.firstTransaction) &&
+ layer->isSimpleBufferUpdate(s);
+ if (allowLatchUnsignaled) {
+ ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->name.c_str());
+ ready = TransactionReadiness::NotReadyUnsignaled;
+ } else {
+ ready = TransactionReadiness::NotReady;
+ auto& listener = s.bufferData->releaseBufferListener;
+ if (listener &&
+ (flushState.queueProcessTime - transaction.postTime) >
+ std::chrono::nanoseconds(4s).count()) {
+ mTransactionHandler
+ .onTransactionQueueStalled(transaction.id, listener,
+ "Buffer processing hung up due to stuck "
+ "fence. Indicates GPU hang");
+ }
+ ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+ }
+ return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
+ });
+ return ready;
+}
+
void SurfaceFlinger::addTransactionReadyFilters() {
mTransactionHandler.addTransactionReadyFilter(
std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
- mTransactionHandler.addTransactionReadyFilter(
- std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
+ if (mLayerLifecycleManagerEnabled) {
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this,
+ std::placeholders::_1));
+ } else {
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheckLegacy, this,
+ std::placeholders::_1));
+ }
}
// For tests only
bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
+ mTransactionHandler.collectTransactions();
std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
return applyTransactions(transactions, vsyncId);
}
@@ -4429,18 +4620,13 @@
predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
}
-bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
- size_t numStates, bool firstTransaction) const {
+bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t numStates,
+ bool firstTransaction) const {
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
ATRACE_FORMAT_INSTANT("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
return false;
}
- if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
- ATRACE_FORMAT_INSTANT("%s: true (LatchUnsignaledConfig::Always)", __func__);
- return true;
- }
-
// We only want to latch unsignaled when a single layer is updated in this
// transaction (i.e. not a blast sync transaction).
if (numStates != 1) {
@@ -4467,7 +4653,7 @@
}
}
- return layer->isSimpleBufferUpdate(state);
+ return true;
}
status_t SurfaceFlinger::setTransactionState(
@@ -5597,7 +5783,7 @@
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
{"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
{"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
- {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
+ {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
@@ -5615,17 +5801,56 @@
// traversals, which can result in use-after-frees.
std::string compositionLayers;
mScheduler
- ->schedule([&] {
- StringAppendF(&compositionLayers, "Composition layers\n");
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto* compositionState = layer->getCompositionState();
- if (!compositionState || !compositionState->isVisible) return;
+ ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ if (!mLayerLifecycleManagerEnabled) {
+ StringAppendF(&compositionLayers, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto* compositionState = layer->getCompositionState();
+ if (!compositionState || !compositionState->isVisible) return;
+ android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n",
+ layer,
+ layer->getDebugName()
+ ? layer->getDebugName()
+ : "<unknown>");
+ compositionState->dump(compositionLayers);
+ });
+ } else {
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (snapshot->hasSomethingToDraw()) {
+ if (lastPrintedLayerStackHeader !=
+ snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader =
+ snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id
+ << "\n";
+ }
+ out << " " << *snapshot << "\n";
+ }
+ });
- android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
- layer->getDebugName() ? layer->getDebugName()
- : "<unknown>");
- compositionState->dump(compositionLayers);
- });
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot(
+ [&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader !=
+ snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader =
+ snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id
+ << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
+
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
+ compositionLayers = out.str();
+ dumpHwcLayersMinidump(compositionLayers);
+ }
})
.get();
@@ -5955,7 +6180,7 @@
result.append(future.get());
}
-void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const {
+void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
@@ -5967,7 +6192,33 @@
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+ mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDumpLegacy(result, ref); });
+ result.append("\n");
+ }
+}
+
+void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
+ for (const auto& [token, display] : mDisplays) {
+ const auto displayId = HalDisplayId::tryCast(display->getId());
+ if (!displayId) {
+ continue;
+ }
+
+ StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+ displayId == mActiveDisplayId ? "active" : "inactive");
+ Layer::miniDumpHeader(result);
+
+ const DisplayDevice& ref = *display;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (!snapshot.hasSomethingToDraw() ||
+ ref.getLayerStack() != snapshot.outputFilter.layerStack) {
+ return;
+ }
+ auto it = mLegacyLayers.find(snapshot.sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
+ it->second->miniDump(result, snapshot, ref);
+ });
result.append("\n");
}
}
@@ -6016,7 +6267,10 @@
* Dump the visible layer list
*/
colorizer.bold(result);
- StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
+ StringAppendF(&result, "SurfaceFlinger New Frontend Enabled:%s\n",
+ mLayerLifecycleManagerEnabled ? "true" : "false");
+ StringAppendF(&result, "Active Layers - layers with client handles (count = %zu)\n",
+ mNumLayers.load());
colorizer.reset(result);
result.append(compositionLayers);
@@ -6089,7 +6343,9 @@
}
result.push_back('\n');
- dumpHwcLayersMinidumpLocked(result);
+ if (mLegacyFrontEndEnabled) {
+ dumpHwcLayersMinidumpLockedLegacy(result);
+ }
{
DumpArgs plannerArgs;
@@ -6266,9 +6522,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1042 are currently used for backdoors. The code
+ // Numbers from 1000 to 1044 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1042) {
+ if (code >= 1000 && code <= 1044) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6729,6 +6985,91 @@
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
+ // hdr sdr ratio overlay
+ case 1043: {
+ auto future = mScheduler->schedule(
+ [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ n = data.readInt32();
+ mHdrSdrRatioOverlay = n != 0;
+ switch (n) {
+ case 0:
+ case 1:
+ enableHdrSdrRatioOverlay(mHdrSdrRatioOverlay);
+ break;
+ default:
+ reply->writeBool(isHdrSdrRatioOverlayEnabled());
+ }
+ });
+ future.wait();
+ return NO_ERROR;
+ }
+
+ case 1044: { // Enable/Disable mirroring from one display to another
+ /*
+ * Mirror one display onto another.
+ * Ensure the source and destination displays are on.
+ * Commands:
+ * 0: Mirror one display to another
+ * 1: Disable mirroring to a previously mirrored display
+ * 2: Disable mirroring on previously mirrored displays
+ *
+ * Ex:
+ * Get the display ids:
+ * adb shell dumpsys SurfaceFlinger --display-id
+ * Mirror first display to the second:
+ * adb shell service call SurfaceFlinger 1044 i64 0 i64 4619827677550801152 i64
+ * 4619827677550801153
+ * Stop mirroring:
+ * adb shell service call SurfaceFlinger 1044 i64 1
+ */
+
+ int64_t arg0 = data.readInt64();
+
+ switch (arg0) {
+ case 0: {
+ // Mirror arg1 to arg2
+ int64_t arg1 = data.readInt64();
+ int64_t arg2 = data.readInt64();
+ // Enable mirroring for one display
+ const auto display1id = DisplayId::fromValue(arg1);
+ auto mirrorRoot = SurfaceComposerClient::getDefault()->mirrorDisplay(
+ display1id.value());
+ auto id2 = DisplayId::fromValue<PhysicalDisplayId>(arg2);
+ const auto token2 = getPhysicalDisplayToken(*id2);
+ ui::LayerStack layerStack;
+ {
+ Mutex::Autolock lock(mStateLock);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(token2);
+ layerStack = display->getLayerStack();
+ }
+ SurfaceComposerClient::Transaction t;
+ t.setDisplayLayerStack(token2, layerStack);
+ t.setLayer(mirrorRoot, INT_MAX); // Top-most layer
+ t.setLayerStack(mirrorRoot, layerStack);
+ t.apply();
+
+ mMirrorMapForDebug.emplace_or_replace(arg2, mirrorRoot);
+ break;
+ }
+
+ case 1: {
+ // Disable mirroring for arg1
+ int64_t arg1 = data.readInt64();
+ mMirrorMapForDebug.erase(arg1);
+ break;
+ }
+
+ case 2: {
+ // Disable mirroring for all displays
+ mMirrorMapForDebug.clear();
+ break;
+ }
+
+ default:
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+ }
}
}
return err;
@@ -7645,7 +7986,13 @@
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
+ setDesiredActiveMode({preferredMode, .emitEvent = true}, force);
+
+ // Update the frameRateOverride list as the display render rate might have changed
+ if (mScheduler->updateFrameRateOverrides(/*consideredSignals*/ {}, preferredMode.fps)) {
+ triggerOnFrameRateOverridesChanged();
+ }
+
return NO_ERROR;
}
@@ -7746,6 +8093,7 @@
if (mTransactionTracing) {
mTransactionTracing->onLayerRemoved(layer->getSequence());
}
+ mScheduler->onLayerDestroyed(layer);
}
void SurfaceFlinger::onLayerUpdate() {
@@ -7831,6 +8179,16 @@
}
}
+void SurfaceFlinger::enableHdrSdrRatioOverlay(bool enable) {
+ for (const auto& [id, display] : mPhysicalDisplays) {
+ if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+ if (const auto device = getDisplayDeviceLocked(id)) {
+ device->enableHdrSdrRatioOverlay(enable);
+ }
+ }
+ }
+}
+
int SurfaceFlinger::getGpuContextPriority() {
return getRenderEngine().getContextPriority();
}
@@ -7841,7 +8199,7 @@
if (presentLatency.count() % refreshRate.getPeriodNsecs()) {
pipelineDepth++;
}
- return std::max(1ll, pipelineDepth - 1);
+ return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1));
}
status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
@@ -8315,6 +8673,7 @@
// 2. Transactions and created layers do not share a lock. To prevent applying
// transactions with layers still in the createdLayer queue, flush the transactions
// before committing the created layers.
+ mTransactionHandler.collectTransactions();
update.transactions = mTransactionHandler.flushTransactions();
{
// TODO(b/238781169) lockless queue this and keep order.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f1759a5..b07910d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -227,6 +227,10 @@
// FramebufferSurface
static int64_t maxFrameBufferAcquiredBuffers;
+ // Controls the minimum acquired buffers SurfaceFlinger will suggest via
+ // ISurfaceComposer.getMaxAcquiredBufferCount().
+ static int64_t minAcquiredBuffers;
+
// Controls the maximum width and height in pixels that the graphics pipeline can support for
// GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
static uint32_t maxGraphicsWidth;
@@ -646,6 +650,7 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
void triggerOnFrameRateOverridesChanged() override;
+ void onChoreographerAttached() override;
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
@@ -672,6 +677,8 @@
bool mRefreshRateOverlayRenderRate = false;
// Show render rate overlay offseted to the middle of the screen (e.g. for circular displays)
bool mRefreshRateOverlayShowInMiddle = false;
+ // Show hdr sdr ratio overlay
+ bool mHdrSdrRatioOverlay = false;
void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
REQUIRES(mStateLock);
@@ -718,10 +725,9 @@
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
const std::vector<std::pair<Layer*, LayerFE*>>& layers);
- bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
- bool transactionsFlushed, bool& out)
- REQUIRES(kMainThreadContext);
- bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed,
+ bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
+ bool& out) REQUIRES(kMainThreadContext);
+ bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
@@ -765,6 +771,9 @@
TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
+ TransactionHandler::TransactionReadiness transactionReadyBufferCheckLegacy(
+ const TransactionHandler::TransactionFlushState& flushState)
+ REQUIRES(kMainThreadContext);
TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
@@ -789,8 +798,7 @@
void commitOffscreenLayers();
static LatchUnsignaledConfig getLatchUnsignaledConfig();
- bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
- bool firstTransaction) const;
+ bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1056,7 +1064,8 @@
*/
void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
std::string& result) const REQUIRES(mStateLock);
- void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock);
+ void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext);
+ void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
void listLayersLocked(std::string& result) const;
@@ -1187,6 +1196,7 @@
bool mUpdateInputInfo = false;
bool mSomeChildrenChanged;
bool mForceTransactionDisplayChange = false;
+ bool mUpdateAttachedChoreographer = false;
// Set if LayerMetadata has changed since the last LayerMetadata snapshot.
bool mLayerMetadataSnapshotNeeded = false;
@@ -1293,8 +1303,6 @@
ui::Dataspace mDefaultCompositionDataspace;
ui::Dataspace mWideColorGamutCompositionDataspace;
- ui::Dataspace mColorSpaceAgnosticDataspace;
- float mDimmingRatio = -1.f;
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
std::atomic<int> mNumTrustedPresentationListeners = 0;
@@ -1342,6 +1350,8 @@
void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
+ void enableHdrSdrRatioOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
+
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
@@ -1387,6 +1397,10 @@
return hasDisplay(
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
}
+ bool isHdrSdrRatioOverlayEnabled() const REQUIRES(mStateLock) {
+ return hasDisplay(
+ [](const auto& display) { return display.isHdrSdrRatioOverlayEnabled(); });
+ }
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
std::optional<ui::LayerStack> layerStack, uint32_t uid,
std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
@@ -1429,6 +1443,10 @@
// WindowInfo ids visible during the last commit.
std::unordered_set<int32_t> mVisibleWindowIds;
+
+ // Mirroring
+ // Map of displayid to mirrorRoot
+ ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 96c8b54..66c8f33 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -227,14 +227,6 @@
return static_cast<int32_t>(defaultValue);
}
-int64_t color_space_agnostic_dataspace(Dataspace defaultValue) {
- auto temp = SurfaceFlingerProperties::color_space_agnostic_dataspace();
- if (temp.has_value()) {
- return *temp;
- }
- return static_cast<int64_t>(defaultValue);
-}
-
bool refresh_rate_switching(bool defaultValue) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 951f8f8..a080420 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -71,9 +71,6 @@
int32_t wcg_composition_pixel_format(
android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
-int64_t color_space_agnostic_dataspace(
- android::hardware::graphics::common::V1_2::Dataspace defaultValue);
-
bool refresh_rate_switching(bool defaultValue);
int32_t set_idle_timer_ms(int32_t defaultValue);
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index fc6c4f3..5512734 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -13,6 +13,21 @@
},
{
"name": "libscheduler_test"
+ },
+ {
+ "name": "CtsGraphicsTestCases",
+ // flaky on mixed gsi builds
+ "options": [
+ {
+ "exclude-filter": "android.graphics.cts.CameraGpuTest#testCameraImageCaptureAndRendering"
+ },
+ {
+ "exclude-filter": "android.graphics.cts.AnimatorLeakTest#testPauseResume"
+ }
+ ]
+ },
+ {
+ "name": "CtsSurfaceControlTests"
}
],
"hwasan-presubmit": [
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 4686eed..c3141be 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -7,14 +7,9 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library {
- name: "libtimestats",
- srcs: [
- "TimeStats.cpp",
- ],
- header_libs: [
- "libscheduler_headers",
- ],
+cc_defaults {
+ name: "libtimestats_deps",
+
shared_libs: [
"android.hardware.graphics.composer@2.4",
"libbase",
@@ -22,17 +17,34 @@
"liblog",
"libprotobuf-cpp-lite",
"libtimestats_atoms_proto",
- "libtimestats_proto",
"libui",
"libutils",
],
+
+ static_libs: [
+ "libtimestats_proto",
+ ],
+
+ export_static_lib_headers: [
+ "libtimestats_proto",
+ ],
+}
+
+cc_library {
+ name: "libtimestats",
+ defaults: [
+ "libtimestats_deps",
+ ],
+ srcs: [
+ "TimeStats.cpp",
+ ],
+ header_libs: [
+ "libscheduler_headers",
+ ],
export_include_dirs: ["."],
export_header_lib_headers: [
"libscheduler_headers",
],
- export_shared_lib_headers: [
- "libtimestats_proto",
- ],
cppflags: [
"-Wall",
"-Werror",
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 630cef1..368cb41 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -106,7 +106,8 @@
atom->set_client_composition_frames(mTimeStats.clientCompositionFramesLegacy);
atom->set_display_on_millis(mTimeStats.displayOnTimeLegacy);
atom->set_animation_millis(mTimeStats.presentToPresentLegacy.totalTime());
- atom->set_event_connection_count(mTimeStats.displayEventConnectionsCountLegacy);
+ // Deprecated
+ atom->set_event_connection_count(0);
*atom->mutable_frame_duration() =
histogramToProto(mTimeStats.frameDurationLegacy.hist, mMaxPulledHistogramBuckets);
*atom->mutable_render_engine_timing() =
@@ -356,16 +357,6 @@
mTimeStats.refreshRateSwitchesLegacy++;
}
-void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
-
- std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.displayEventConnectionsCountLegacy =
- std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
-}
-
static int32_t toMs(nsecs_t nanos) {
int64_t millis =
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
@@ -1072,7 +1063,6 @@
mTimeStats.compositionStrategyPredictedLegacy = 0;
mTimeStats.compositionStrategyPredictionSucceededLegacy = 0;
mTimeStats.refreshRateSwitchesLegacy = 0;
- mTimeStats.displayEventConnectionsCountLegacy = 0;
mTimeStats.displayOnTimeLegacy = 0;
mTimeStats.presentToPresentLegacy.hist.clear();
mTimeStats.frameDurationLegacy.hist.clear();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5f58657..0c227d4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -57,9 +57,6 @@
virtual void incrementMissedFrames() = 0;
// Increments the number of times the display refresh rate changed.
virtual void incrementRefreshRateSwitches() = 0;
- // Records the most up-to-date count of display event connections.
- // The stored count will be the maximum ever recoded.
- virtual void recordDisplayEventConnectionCount(int32_t count) = 0;
// Records the start and end times for a frame.
// The start time is the same as the beginning of a SurfaceFlinger
@@ -253,7 +250,6 @@
void incrementTotalFrames() override;
void incrementMissedFrames() override;
void incrementRefreshRateSwitches() override;
- void recordDisplayEventConnectionCount(int32_t count) override;
void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index cf1ca65..cbbcb91 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -115,7 +115,7 @@
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
result.append("Jank payload for this layer:\n");
result.append(jankPayload.toString());
- result.append("SetFrateRate vote for this layer:\n");
+ result.append("SetFrameRate vote for this layer:\n");
result.append(setFrameRateVote.toString());
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 60aa810..9e97f0d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -175,7 +175,6 @@
int32_t clientCompositionReusedFramesLegacy = 0;
int32_t refreshRateSwitchesLegacy = 0;
int32_t compositionStrategyChangesLegacy = 0;
- int32_t displayEventConnectionsCountLegacy = 0;
int64_t displayOnTimeLegacy = 0;
Histogram presentToPresentLegacy;
Histogram frameDurationLegacy;
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 7132a59..31cd2d7 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -89,7 +89,7 @@
void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
for (auto state = states.begin(); state != states.end();) {
if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
- int result = visitor(state->state, state->externalTexture);
+ int result = visitor(*state);
if (result == STOP_TRAVERSAL) return;
if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
state = states.erase(state);
diff --git a/services/surfaceflinger/Utils/OverlayUtils.h b/services/surfaceflinger/Utils/OverlayUtils.h
new file mode 100644
index 0000000..0ef0be1
--- /dev/null
+++ b/services/surfaceflinger/Utils/OverlayUtils.h
@@ -0,0 +1,146 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#pragma once
+
+#include "BackgroundExecutor.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#pragma clang diagnostic pop
+
+#include <gui/SurfaceComposerClient.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+inline constexpr int kDigitWidth = 64;
+inline constexpr int kDigitHeight = 100;
+inline constexpr int kDigitSpace = 16;
+
+// HdrSdrRatioOverlay re-uses this value though it doesn't really need such amount buffer.
+// for output good-looking and code conciseness.
+inline constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+inline constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
+inline constexpr int kBufferHeight = kDigitHeight;
+
+class SurfaceControl;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+ explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+
+ ~SurfaceControlHolder() {
+ // Hand the sp<SurfaceControl> to the helper thread to release the last
+ // reference. This makes sure that the SurfaceControl is destructed without
+ // SurfaceFlinger::mStateLock held.
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+ }
+
+ static std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder(const String8& name) {
+ sp<SurfaceControl> surfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(name, kBufferWidth, kBufferHeight, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+ }
+
+ const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+ sp<SurfaceControl> mSurfaceControl;
+};
+
+// Helper class to draw digit and decimal point.
+class SegmentDrawer {
+public:
+ enum class Segment {
+ Upper,
+ UpperLeft,
+ UpperRight,
+ Middle,
+ LowerLeft,
+ LowerRight,
+ Bottom,
+ DecimalPoint
+ };
+ static void drawSegment(Segment segment, int left, SkColor color, SkCanvas& canvas) {
+ const SkRect rect = [&]() {
+ switch (segment) {
+ case Segment::Upper:
+ return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
+ case Segment::UpperLeft:
+ return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2.);
+ case Segment::UpperRight:
+ return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
+ kDigitHeight / 2.);
+ case Segment::Middle:
+ return SkRect::MakeLTRB(left, kDigitHeight / 2. - kDigitSpace / 2.,
+ left + kDigitWidth,
+ kDigitHeight / 2. + kDigitSpace / 2.);
+ case Segment::LowerLeft:
+ return SkRect::MakeLTRB(left, kDigitHeight / 2., left + kDigitSpace,
+ kDigitHeight);
+ case Segment::LowerRight:
+ return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2.,
+ left + kDigitWidth, kDigitHeight);
+ case Segment::Bottom:
+ return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
+ kDigitHeight);
+ case Segment::DecimalPoint:
+ return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitSpace,
+ kDigitHeight);
+ }
+ }();
+
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas.drawRect(rect, paint);
+ }
+
+ static void drawDigit(int digit, int left, SkColor color, SkCanvas& canvas) {
+ if (digit < 0 || digit > 9) return;
+
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::Upper, left, color, canvas);
+ if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+ drawSegment(Segment::UpperLeft, left, color, canvas);
+ if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::UpperRight, left, color, canvas);
+ if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Middle, left, color, canvas);
+ if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+ drawSegment(Segment::LowerLeft, left, color, canvas);
+ if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+ digit == 7 || digit == 8 || digit == 9)
+ drawSegment(Segment::LowerRight, left, color, canvas);
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Bottom, left, color, canvas);
+ }
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index f76a8d7..0f9060d 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -138,3 +138,18 @@
"surfaceflinger_frametracer_fuzzer.cpp",
],
}
+
+cc_fuzz {
+ name: "surfaceflinger_service_fuzzer",
+ defaults: [
+ "surfaceflinger_fuzz_defaults",
+ "service_fuzzer_defaults",
+ "fuzzer_disable_leaks",
+ ],
+ srcs: [
+ "surfaceflinger_service_fuzzer.cpp",
+ ],
+ fuzz_config: {
+ triage_assignee: "waghpawan@google.com",
+ },
+}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index 9fac14e..f22315a 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -597,7 +597,7 @@
mFdp.ConsumeBool() ? hal::Vsync::ENABLE : hal::Vsync::DISABLE);
mHwc.isConnected(mPhysicalDisplayId);
- mHwc.getModes(mPhysicalDisplayId);
+ mHwc.getModes(mPhysicalDisplayId, mFdp.ConsumeIntegral<int32_t>());
mHwc.getActiveMode(mPhysicalDisplayId);
mHwc.getColorModes(mPhysicalDisplayId);
mHwc.hasCapability(mFdp.PickValueInArray(kCapability));
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 80943b5..b2d4131 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -24,7 +24,6 @@
namespace android::fuzz {
static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = {
- LatchUnsignaledConfig::Always,
LatchUnsignaledConfig::AutoSingleLayer,
LatchUnsignaledConfig::Disabled,
};
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 36095b4..28ac664 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -796,6 +796,7 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
+ void onChoreographerAttached() override {}
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger =
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp
new file mode 100644
index 0000000..849a896
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_service_fuzzer.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+
+#include "SurfaceFlinger.h"
+#include "SurfaceFlingerDefaultFactory.h"
+
+using namespace android;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ DefaultFactory factory;
+ sp<SurfaceFlinger> flinger = sp<SurfaceFlinger>::make(factory);
+ flinger->init();
+
+ sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
+ fuzzService({flinger, composerAIDL}, FuzzedDataProvider(data, size));
+ return 0;
+}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 689f51a..be29be4 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -199,6 +199,7 @@
# useColorManagement indicates whether SurfaceFlinger should manage color
# by switching to appropriate color mode automatically depending on the
# Dataspace of the surfaces on screen.
+# DEPRECATED: SurfaceFlinger is always color managed.
prop {
api_name: "use_color_management"
type: Boolean
@@ -207,7 +208,7 @@
prop_name: "ro.surface_flinger.use_color_management"
}
-# The following four propertiess define:
+# The following four properties define:
# Returns the default data space and pixel format that SurfaceFlinger
# expects to receive and output as well as the wide color gamut data space
# and pixel format for wide color gamut surfaces.
@@ -276,7 +277,7 @@
# The variable works only when useColorManagement is specified. If
# unspecified, the data space follows what SurfaceFlinger expects for
# surfaces when useColorManagement is specified.
-
+# DEPRECATED: do not use
prop {
api_name: "color_space_agnostic_dataspace"
type: Long
@@ -472,6 +473,16 @@
prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
}
+# Controls the minimum acquired buffers SurfaceFlinger will suggest via
+# ISurfaceComposer.getMaxAcquiredBufferCount().
+prop {
+ api_name: "min_acquired_buffers"
+ type: Long
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.min_acquired_buffers"
+}
+
# When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
# buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
# Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
@@ -484,4 +495,3 @@
access: Readonly
prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
}
-
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9660ff3..ba88acc 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -97,6 +97,11 @@
prop_name: "ro.surface_flinger.max_virtual_display_dimension"
}
prop {
+ api_name: "min_acquired_buffers"
+ type: Long
+ prop_name: "ro.surface_flinger.min_acquired_buffers"
+ }
+ prop {
api_name: "present_time_offset_from_vsync_ns"
type: Long
prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 62b539a..b5168b0 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -37,8 +37,9 @@
"DisplayConfigs_test.cpp",
"DisplayEventReceiver_test.cpp",
"EffectLayer_test.cpp",
- "LayerBorder_test.cpp",
+ "HdrSdrRatioOverlay_test.cpp",
"InvalidHandles_test.cpp",
+ "LayerBorder_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
"LayerState_test.cpp",
diff --git a/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp
new file mode 100644
index 0000000..77a8f9c
--- /dev/null
+++ b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp
@@ -0,0 +1,89 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <chrono>
+
+using ::std::literals::chrono_literals::operator""s;
+
+static constexpr int kHdrSdrRatioOverlayCode = 1043;
+static constexpr int kHdrSdrRatioOverlayEnable = 1;
+static constexpr int kHdrSdrRatioOverlayDisable = 0;
+static constexpr int kHdrSdrRatioOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowHdrSdrRatioPreferenceController
+static_assert(kHdrSdrRatioOverlayCode == 1043);
+static_assert(kHdrSdrRatioOverlayEnable == 1);
+static_assert(kHdrSdrRatioOverlayDisable == 0);
+static_assert(kHdrSdrRatioOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ Parcel request;
+ request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ request.writeInt32(command);
+ ASSERT_EQ(NO_ERROR,
+ IInterface::asBinder(sf)->transact(kHdrSdrRatioOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+ Parcel reply;
+ sendCommandToSf(kHdrSdrRatioOverlayQuery, reply);
+ return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+ static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+ static constexpr auto kIterations = 10;
+ for (int i = 0; i < kIterations; i++) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+ std::this_thread::sleep_for(kTimeout / kIterations);
+ }
+}
+
+void toggleOverlay(bool enabled) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+
+ Parcel reply;
+ const auto command = enabled ? kHdrSdrRatioOverlayEnable : kHdrSdrRatioOverlayDisable;
+ sendCommandToSf(command, reply);
+ waitForOverlay(enabled);
+ ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(HdrSdrRatioOverlayTest, enableAndDisableOverlay) {
+ toggleOverlay(true);
+ toggleOverlay(false);
+
+ toggleOverlay(true);
+ toggleOverlay(false);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 40a5d57..18bd3b9 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -289,7 +289,7 @@
IPCThreadState::self()->joinThreadPool();
[&]() { exit(0); }();
}
- sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+ sp<IBinder> binder = defaultServiceManager()->waitForService(serviceName);
remote = interface_cast<IIPCTest>(binder);
remote->setDeathToken(mDeathRecipient);
}
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index cbd54e7..03de8d0 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -184,6 +184,35 @@
}
}
+TEST_F(LayerTransactionTest, CommitCallbackCalledOnce) {
+ auto callCount = 0;
+ auto commitCallback =
+ [&callCount](void* /* context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) mutable {
+ callCount++;
+ };
+
+ // Create two transactions that both contain the same callback id.
+ Transaction t1;
+ t1.addTransactionCommittedCallback(commitCallback, nullptr);
+ Parcel parcel;
+ t1.writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ Transaction t2;
+ t2.readFromParcel(&parcel);
+
+ // Apply the two transactions. There is a race here as we can't guarantee that the two
+ // transactions will be applied within the same SurfaceFlinger commit. If the transactions are
+ // applied within the same commit then we verify that callback ids are deduplicated within a
+ // single commit. Otherwise, we verify that commit callbacks are deduplicated across separate
+ // commits.
+ t1.apply();
+ t2.apply(/*synchronous=*/true);
+
+ ASSERT_EQ(callCount, 1);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
index 7077523..f1bb231 100644
--- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -85,7 +85,7 @@
ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags());
}
-TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90inactive) {
auto displayToken = mOuterDisplay->getDisplayToken().promote();
mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
@@ -95,7 +95,7 @@
ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
}
-TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBothInnerActive) {
auto displayToken = mInnerDisplay->getDisplayToken().promote();
mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
@@ -110,7 +110,7 @@
ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
}
-TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) {
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBothOuterActive) {
mFlinger.mutableActiveDisplayId() = kOuterDisplayId;
auto displayToken = mInnerDisplay->getDisplayToken().promote();
mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 86af303..fa5fa95 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -161,7 +161,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-cpp",
+ "android.hardware.power-V4-ndk",
"libaidlcommonsupport",
"libcompositionengine_mocks",
"libcompositionengine",
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 5fed9b4..91c6239 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -634,31 +634,6 @@
expectVSyncCallbackScheduleReceived(false);
}
-TEST_F(EventThreadTest, tracksEventConnections) {
- setupEventThread(VSYNC_PERIOD);
-
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
- ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
- mThread->setVsyncRate(1, errorConnection);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
- ConnectionEventRecorder secondConnectionEventRecorder{0};
- sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder);
- mThread->setVsyncRate(1, secondConnection);
- EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
-
- // EventThread should enable vsync callbacks.
- expectVSyncCallbackScheduleReceived(true);
-
- // The first event will be seen by the connection, which then returns an error.
- onVSyncEvent(123, 456, 789);
- expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
- expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
- 1u);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
-}
-
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
setupEventThread(VSYNC_PERIOD);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d26ef3c..8911430 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -1198,7 +1198,7 @@
TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
// Layer specific increment
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -1234,8 +1234,8 @@
auto protoDroppedSurfaceFrameActualStart =
createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken,
displayFrameToken1, sPidOne, sLayerNameOne,
- FrameTimelineEvent::PRESENT_DROPPED, false, false,
- FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::PRESENT_DROPPED, true, false,
+ FrameTimelineEvent::JANK_DROPPED,
FrameTimelineEvent::PREDICTION_VALID, true);
auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
@@ -1470,7 +1470,7 @@
createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
- FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::JANK_DROPPED,
FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index da00377..8a45f17 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -53,6 +53,7 @@
using Hwc2::Config;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
+using hal::IComposerClient;
using ::testing::_;
using ::testing::DoAll;
using ::testing::ElementsAreArray;
@@ -119,6 +120,157 @@
}
}
+TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) {
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ constexpr hal::HWConfigId kConfigId = 42;
+ constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
+
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
+ _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE)));
+
+ // Optional Parameters UNSUPPORTED
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}),
+ Return(HalError::NONE)));
+
+ auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+
+ // Optional parameters are supported
+ constexpr int32_t kDpi = 320;
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ // DPI values are scaled by 1000 in the legacy implementation.
+ EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f);
+ EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f);
+ }
+}
+
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations) {
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ constexpr hal::HWConfigId kConfigId = 42;
+ constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(true));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
+ hal::DisplayConfiguration displayConfiguration;
+ displayConfiguration.configId = kConfigId;
+ displayConfiguration.configGroup = kConfigGroup;
+ displayConfiguration.height = kHeight;
+ displayConfiguration.width = kWidth;
+ displayConfiguration.vsyncPeriod = kVsyncPeriod;
+
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
+ displayConfiguration}),
+ Return(HalError::NONE)));
+
+ // Optional dpi not supported
+ auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+
+ // Supports optional dpi parameter
+ constexpr int32_t kDpi = 320;
+ displayConfiguration.dpi = {kDpi, kDpi};
+
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
+ displayConfiguration}),
+ Return(HalError::NONE)));
+
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, kDpi);
+ EXPECT_EQ(modes.front().dpiY, kDpi);
+ }
+}
+
TEST_F(HWComposerTest, onVsync) {
constexpr hal::HWDisplayId kHwcDisplayId = 1;
expectHotplugConnect(kHwcDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 4301186..e475b84 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -308,6 +308,31 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateSelectionPriority = priority;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRate = frameRate;
+ transactions.back().states.front().state.frameRateCompatibility = compatibility;
+ transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 5da893e..a581d5b 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -20,6 +20,7 @@
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerSnapshotBuilder.h"
+#include "Layer.h"
#include "LayerHierarchyTest.h"
#define UPDATE_AND_VERIFY(BUILDER, ...) \
@@ -467,4 +468,132 @@
EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size());
}
+TEST_F(LayerSnapshotTest, snashotContainsMetadataFromLayerCreationArgs) {
+ LayerCreationArgs args(std::make_optional<uint32_t>(200));
+ args.name = "testlayer";
+ args.addToRoot = true;
+ args.metadata.setInt32(42, 24);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(args));
+ EXPECT_TRUE(layers.back()->metadata.has(42));
+ EXPECT_EQ(layers.back()->metadata.getInt32(42, 0), 24);
+ mLifecycleManager.addLayers(std::move(layers));
+
+ std::vector<uint32_t> expected = STARTING_ZORDER;
+ expected.push_back(200);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ EXPECT_TRUE(mSnapshotBuilder.getSnapshot(200)->layerMetadata.has(42));
+ EXPECT_EQ(mSnapshotBuilder.getSnapshot(200)->layerMetadata.getInt32(42, 0), 24);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionPriorityPassedToChildLayers) {
+ setFrameRateSelectionPriority(11, 1);
+
+ setFrameRateSelectionPriority(12, 2);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 2);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 2);
+
+ // reparent and verify the child gets the new parent's framerate selection priority
+ reparentLayer(122, 11);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionPriority, Layer::PRIORITY_UNSET);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionPriority, 1);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionPriority, 2);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionPriority, 1);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionPriority, 1);
+}
+
+TEST_F(LayerSnapshotTest, framerate) {
+ setFrameRate(11, 244.f, 0, 0);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent is gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer and children get the requested votes
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // reparent and verify the child gets the new parent's framerate
+ reparentLayer(122, 11);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ // verify parent is gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+
+ // verify layer and children get the requested votes
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // reparent and verify the new parent gets no vote
+ reparentLayer(11, 2);
+ expected = {1, 12, 121, 13, 2, 11, 111, 122, 1221};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ // verify old parent has invalid framerate (default)
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify new parent get no vote
+ EXPECT_FALSE(getSnapshot({.id = 2})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 2})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 2})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer and children get the requested votes (unchanged)
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 0d66d59..85f66f4 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -18,10 +18,10 @@
#define LOG_TAG "PowerAdvisorTest"
#include <DisplayHardware/PowerAdvisor.h>
-#include <compositionengine/Display.h>
-#include <ftl/fake_guard.h>
+#include <binder/Status.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
#include <ui/DisplayId.h>
#include <chrono>
#include "TestableSurfaceFlinger.h"
@@ -50,7 +50,7 @@
TestableSurfaceFlinger mFlinger;
std::unique_ptr<PowerAdvisor> mPowerAdvisor;
MockPowerHalController* mMockPowerHalController;
- sp<MockIPowerHintSession> mMockPowerHintSession;
+ std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
};
void PowerAdvisorTest::SetUp() {
@@ -64,13 +64,14 @@
void PowerAdvisorTest::startPowerHintSession() {
const std::vector<int32_t> threadIds = {1, 2, 3};
- mMockPowerHintSession = android::sp<NiceMock<MockIPowerHintSession>>::make();
+ mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(
- Return(HalResult<sp<IPowerHintSession>>::fromStatus(binder::Status::ok(),
- mMockPowerHintSession)));
+ .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(binder::Status::ok(), mMockPowerHintSession)));
mPowerAdvisor->enablePowerHintSession(true);
mPowerAdvisor->startPowerHintSession(threadIds);
+ ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
+ .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
@@ -123,8 +124,8 @@
EXPECT_CALL(*mMockPowerHintSession,
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
- .Times(1);
-
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
mPowerAdvisor->setDisplays(displayIds);
@@ -163,7 +164,8 @@
EXPECT_CALL(*mMockPowerHintSession,
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
- .Times(1);
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -205,7 +207,8 @@
EXPECT_CALL(*mMockPowerHintSession,
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
- .Times(1);
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 682c998..3200003 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -30,6 +30,10 @@
#include "mock/MockLayer.h"
#include "mock/MockSchedulerCallback.h"
+#include <FrontEnd/LayerHierarchy.h>
+
+#include "FpsOps.h"
+
namespace android::scheduler {
using android::mock::createDisplayMode;
@@ -42,6 +46,10 @@
using MockEventThread = android::mock::EventThread;
using MockLayer = android::mock::MockLayer;
+using LayerHierarchy = surfaceflinger::frontend::LayerHierarchy;
+using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder;
+using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState;
+
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
@@ -83,6 +91,7 @@
mock::SchedulerCallback mSchedulerCallback;
TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
+ surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
@@ -149,10 +158,6 @@
EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
-
- static constexpr size_t kEventConnections = 5;
- EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
}
TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) {
@@ -199,7 +204,8 @@
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
}
TEST_F(SchedulerTest, updateDisplayModes) {
@@ -249,6 +255,11 @@
EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+
+ const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers();
+ mFlinger.mutableMinAcquiredBuffers() = 2;
+ EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+ mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers;
}
MATCHER(Is120Hz, "") {
@@ -273,11 +284,13 @@
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
// No-op if layer requirements have not changed.
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
}
TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
@@ -432,4 +445,344 @@
}
}
+class AttachedChoreographerTest : public SchedulerTest {
+protected:
+ void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
+ Fps expectedChoreographerFps);
+};
+
+TEST_F(AttachedChoreographerTest, registerSingle) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ const sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const auto handle = layer->getHandle();
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
+
+ EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_))
+ .WillOnce(Return(0))
+ .WillOnce(Return(0));
+
+ const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread);
+ const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread);
+ EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ .WillOnce(Return(mockConnection1))
+ .WillOnce(Return(mockConnection2));
+
+ const sp<IDisplayEventConnection> connection1 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+ const sp<IDisplayEventConnection> connection2 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+
+ EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(2u,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, registerMultipleOnDifferentLayers) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
+ const sp<IDisplayEventConnection> connection1 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer1->getHandle());
+ const sp<IDisplayEventConnection> connection2 =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer2->getHandle());
+
+ EXPECT_EQ(2u, mScheduler->mutableAttachedChoreographers().size());
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer1->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer1->getSequence()]
+ .connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer1->getSequence()].frameRate.isValid());
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer2->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer2->getSequence()]
+ .connections.size());
+ EXPECT_FALSE(
+ mScheduler->mutableAttachedChoreographers()[layer2->getSequence()].frameRate.isValid());
+}
+
+TEST_F(AttachedChoreographerTest, removedWhenConnectionIsGone) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(1u,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.size());
+
+ // The connection is used all over this test, so it is quite hard to release it from here.
+ // Instead, we just do a small shortcut.
+ {
+ EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
+ sp<MockEventThreadConnection> mockConnection =
+ sp<MockEventThreadConnection>::make(mEventThread);
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.clear();
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].connections.emplace(
+ mockConnection);
+ }
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ mScheduler->updateAttachedChoreographers(hierarchy, 60_Hz);
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+}
+
+TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) {
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ const sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ layer.clear();
+ mFlinger.mutableLayersPendingRemoval().clear();
+ EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty());
+}
+
+void AttachedChoreographerTest::frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility,
+ Fps displayFps,
+ Fps expectedChoreographerFps) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+
+ layerState.frameRate = layerFps.getValue();
+ layerState.frameRateCompatibility = frameRateCompatibility;
+
+ mScheduler->updateAttachedChoreographers(hierarchy, displayFps);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+ EXPECT_EQ(expectedChoreographerFps,
+ mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+ EXPECT_EQ(expectedChoreographerFps, mEventThreadConnection->frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateDefault) {
+ Fps layerFps = 30_Hz;
+ int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+ Fps displayFps = 60_Hz;
+ Fps expectedChoreographerFps = 30_Hz;
+
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+ layerFps = Fps::fromValue(32.7f);
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateExact) {
+ Fps layerFps = 30_Hz;
+ int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_EXACT;
+ Fps displayFps = 60_Hz;
+ Fps expectedChoreographerFps = 30_Hz;
+
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+ layerFps = Fps::fromValue(32.7f);
+ expectedChoreographerFps = {};
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateExactOrMultiple) {
+ Fps layerFps = 30_Hz;
+ int8_t frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ Fps displayFps = 60_Hz;
+ Fps expectedChoreographerFps = 30_Hz;
+
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+
+ layerFps = Fps::fromValue(32.7f);
+ expectedChoreographerFps = {};
+ frameRateTestScenario(layerFps, frameRateCompatibility, displayFps, expectedChoreographerFps);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParent) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+ layerState.frameRate = (30_Hz).getValue();
+ layerState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+ EXPECT_EQ(30_Hz, mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParent2Children) {
+ const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layer1State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer1Hierarchy(&layer1State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer1Hierarchy, LayerHierarchy::Variant::Attached));
+
+ RequestedLayerState layer2State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer2Hierarchy(&layer2State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer2Hierarchy, LayerHierarchy::Variant::Attached));
+
+ layer1State.frameRate = (30_Hz).getValue();
+ layer1State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ layer2State.frameRate = (20_Hz).getValue();
+ layer2State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+ EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateParentConflictingChildren) {
+ const sp<MockLayer> layer1 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> layer2 = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layer1State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer1Hierarchy(&layer1State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer1Hierarchy, LayerHierarchy::Variant::Attached));
+
+ RequestedLayerState layer2State(LayerCreationArgs(layer1->getSequence()));
+ LayerHierarchy layer2Hierarchy(&layer2State);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&layer2Hierarchy, LayerHierarchy::Variant::Attached));
+
+ layer1State.frameRate = (30_Hz).getValue();
+ layer1State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ layer2State.frameRate = (25_Hz).getValue();
+ layer2State.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(parent->getSequence()));
+
+ EXPECT_EQ(Fps(), mScheduler->mutableAttachedChoreographers()[parent->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateChild) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+ parentState.frameRate = (30_Hz).getValue();
+ parentState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+
+ EXPECT_EQ(30_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+}
+
+TEST_F(AttachedChoreographerTest, setsFrameRateChildNotOverriddenByParent) {
+ const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ const sp<MockLayer> parent = sp<MockLayer>::make(mFlinger.flinger());
+
+ EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
+ sp<IDisplayEventConnection> connection =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+
+ RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
+ LayerHierarchy parentHierarchy(&parentState);
+
+ RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
+ LayerHierarchy hierarchy(&layerState);
+ parentHierarchy.mChildren.push_back(
+ std::make_pair(&hierarchy, LayerHierarchy::Variant::Attached));
+
+ parentState.frameRate = (30_Hz).getValue();
+ parentState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ layerState.frameRate = (60_Hz).getValue();
+ layerState.frameRateCompatibility = ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT;
+
+ mScheduler->updateAttachedChoreographers(parentHierarchy, 120_Hz);
+
+ ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
+
+ EXPECT_EQ(60_Hz, mScheduler->mutableAttachedChoreographers()[layer->getSequence()].frameRate);
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index e176546..703bdda 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -49,9 +49,17 @@
mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
mFlinger.configureAndCommit();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(
+ TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
.setRefreshRateSelector(std::move(selectorPtr))
- .inject();
+ .inject(std::move(vsyncController), std::move(vsyncTracker));
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
// will call setActiveConfig instead of setActiveConfigWithConstraints.
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 4e9f293..22b72f9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -23,12 +23,12 @@
#include "DisplayTransactionTestHelpers.h"
#include "FakeDisplayInjector.h"
-#include <android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/Boost.h>
namespace android {
namespace {
-using android::hardware::power::Boost;
+using aidl::android::hardware::power::Boost;
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
using namespace std::chrono_literals;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a978984..f3c9d0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -39,7 +39,7 @@
TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
- /* modulatorPtr */ nullptr, callback) {}
+ sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
@@ -104,6 +104,7 @@
auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
auto& mutableLayerHistory() { return mLayerHistory; }
+ auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
@@ -168,6 +169,12 @@
: VsyncSchedule::HwVsyncState::Disabled;
}
+ void updateAttachedChoreographers(
+ const surfaceflinger::frontend::LayerHierarchy& layerHierarchy,
+ Fps displayRefreshRate) {
+ Scheduler::updateAttachedChoreographers(layerHierarchy, displayRefreshRate);
+ }
+
using Scheduler::onHardwareVsyncRequest;
private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index da482d5..9b3a893 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -55,6 +55,12 @@
#include "mock/MockSchedulerCallback.h"
#include "mock/system/window/MockNativeWindow.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
+
namespace android {
struct DisplayStatInfo;
@@ -631,6 +637,10 @@
return SurfaceFlinger::sActiveDisplayRotationFlags;
}
+ auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
+
+ auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
~TestableSurfaceFlinger() {
@@ -641,6 +651,7 @@
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
+ mFlinger->mLayersPendingRemoval.clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
@@ -915,6 +926,13 @@
}
sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
+ return inject(std::make_unique<mock::VsyncController>(),
+ std::make_shared<mock::VSyncTracker>());
+ }
+
+ sp<DisplayDevice> inject(std::unique_ptr<android::scheduler::VsyncController> controller,
+ std::shared_ptr<android::scheduler::VSyncTracker> tracker)
+ NO_THREAD_SAFETY_ANALYSIS {
const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
auto& modes = mDisplayModes;
@@ -979,7 +997,9 @@
if (mFlinger.scheduler() && mRegisterDisplay) {
mFlinger.scheduler()->registerDisplay(physicalId,
- display->holdRefreshRateSelector());
+ display->holdRefreshRateSelector(),
+ std::move(controller),
+ std::move(tracker));
}
display->setActiveMode(activeModeId, fps, fps);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a9ae1d3..86ed233 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1079,7 +1079,6 @@
constexpr size_t TOTAL_FRAMES = 5;
constexpr size_t MISSED_FRAMES = 4;
constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
- constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
@@ -1100,7 +1099,6 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
- mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->recordFrameDuration(1000000, 3000000);
mTimeStats->recordRenderEngineDuration(2000000, 4000000);
@@ -1157,7 +1155,6 @@
EXPECT_EQ(atom.client_composition_frames(), CLIENT_COMPOSITION_FRAMES);
// Display on millis is not checked.
EXPECT_EQ(atom.animation_millis(), 2);
- EXPECT_EQ(atom.event_connection_count(), DISPLAY_EVENT_CONNECTIONS);
EXPECT_THAT(atom.frame_duration(), HistogramEq(buildExpectedHistogram({2}, {1})));
EXPECT_THAT(atom.render_engine_timing(), HistogramEq(buildExpectedHistogram({1, 2}, {1, 1})));
EXPECT_EQ(atom.total_timeline_frames(), 9);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index afb8efb..644b8c7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -15,7 +15,7 @@
*/
#undef LOG_TAG
-#define LOG_TAG "CompositionTest"
+#define LOG_TAG "TransactionApplicationTest"
#include <compositionengine/Display.h>
#include <compositionengine/mock/DisplaySurface.h>
@@ -855,233 +855,13 @@
kExpectedTransactionsPending);
}
-class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
-public:
- void SetUp() override {
- LatchUnsignaledTest::SetUp();
- SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
- }
-};
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId, fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto mixedTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- createComposerState(kLayerId, fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto mixedTransaction =
- createTransactionInfo(kApplyToken,
- {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- createComposerState(kLayerId2, fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged)});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto signaledTransaction2 =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken2,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({signaledTransaction, unsignaledTransaction},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyToken) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({unsignaledTransaction, signaledTransaction},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- const auto unsignaledTransaction2 =
- createTransactionInfo(kApplyToken2,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
- setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, RespectsBackPressureFlag) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kExpectedTransactionsPending = 1u;
- auto layer =
- sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}));
- auto layerHandle = layer->getHandle();
- const auto setBackPressureFlagTransaction =
- createTransactionInfo(kApplyToken1,
- {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged |
- layer_state_t::eFlagsChanged,
- {layerHandle})});
- setTransactionStates({setBackPressureFlagTransaction}, 0u);
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged,
- {layerHandle}),
- });
- const auto unsignaledTransaction2 =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged,
- {layerHandle}),
- });
- setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsPending = 0u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
-
- modulateVsync();
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
-}
-
TEST(TransactionHandlerTest, QueueTransaction) {
TransactionHandler handler;
TransactionState transaction;
transaction.applyToken = sp<BBinder>::make();
transaction.id = 42;
handler.queueTransaction(std::move(transaction));
+ handler.collectTransactions();
std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index d3fb9fc..95004a4 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -51,6 +51,7 @@
~Composer() override;
MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
+ MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override));
MOCK_METHOD0(getCapabilities,
std::vector<aidl::android::hardware::graphics::composer3::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
@@ -70,6 +71,8 @@
MOCK_METHOD4(getDisplayAttribute,
Error(Display, Config config, IComposerClient::Attribute, int32_t*));
MOCK_METHOD2(getDisplayConfigs, Error(Display, std::vector<Config>*));
+ MOCK_METHOD3(getDisplayConfigurations,
+ Error(Display, int32_t, std::vector<DisplayConfiguration>*));
MOCK_METHOD2(getDisplayName, Error(Display, std::string*));
MOCK_METHOD4(getDisplayRequests,
Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
index 0ddc90d..a088aab 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -18,14 +18,14 @@
#include "binder/Status.h"
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <gmock/gmock.h>
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::IPower;
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::Mode;
using android::binder::Status;
-using android::hardware::power::Boost;
-using android::hardware::power::IPower;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::Mode;
namespace android::Hwc2::mock {
@@ -33,18 +33,19 @@
public:
MockIPower();
- MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
- MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
- MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
- MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
- MOCK_METHOD(Status, createHintSession,
+ MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos, sp<IPowerHintSession>* session),
+ int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
- MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index f4ded21..2b9520f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -18,14 +18,14 @@
#include "binder/Status.h"
-#include <android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPower.h>
#include <gmock/gmock.h>
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionHint;
using android::binder::Status;
-using android::hardware::power::IPowerHintSession;
-using android::hardware::power::SessionHint;
-using namespace android::hardware::power;
+using namespace aidl::android::hardware::power;
namespace android::Hwc2::mock {
@@ -33,16 +33,18 @@
public:
MockIPowerHintSession();
- MOCK_METHOD(IBinder*, onAsBinder, (), (override));
- MOCK_METHOD(Status, pause, (), (override));
- MOCK_METHOD(Status, resume, (), (override));
- MOCK_METHOD(Status, close, (), (override));
- MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
- MOCK_METHOD(std::string, getInterfaceHash, (), (override));
- MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
- MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
- MOCK_METHOD(Status, sendHint, (SessionHint), (override));
- MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, pause, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, resume, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, reportActualWorkDuration, (const ::std::vector<WorkDuration>&),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index 8e22f43..68fe3c5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -31,8 +31,8 @@
namespace android::Hwc2::mock {
-using android::hardware::power::Boost;
-using android::hardware::power::Mode;
+using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::Mode;
using android::power::HalResult;
class MockPowerHalController : public power::PowerHalController {
@@ -42,8 +42,9 @@
MOCK_METHOD(void, init, (), (override));
MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override));
MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override));
- MOCK_METHOD(HalResult<sp<hardware::power::IPowerHintSession>>, createHintSession,
- (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override));
+ MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+ createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t),
+ (override));
MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8d57049..9a1a16d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -49,7 +49,6 @@
(const sp<android::EventThreadConnection>&), (const, override));
MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
MOCK_METHOD(void, pauseVsyncCallback, (bool));
- MOCK_METHOD(size_t, getEventThreadConnectionCount, (), (override));
MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 306eb4d..22b2ccc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -27,6 +27,7 @@
MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
+ MOCK_METHOD(void, onChoreographerAttached, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -34,6 +35,7 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
+ void onChoreographerAttached() override {}
};
} // namespace android::scheduler::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 86fbadc..c82e45b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -34,7 +34,6 @@
MOCK_METHOD0(incrementTotalFrames, void());
MOCK_METHOD0(incrementMissedFrames, void());
MOCK_METHOD0(incrementRefreshRateSwitches, void());
- MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t));
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
index b033adb..7e382a3 100644
--- a/services/vibratorservice/TEST_MAPPING
+++ b/services/vibratorservice/TEST_MAPPING
@@ -1,7 +1,27 @@
{
"presubmit": [
{
+ "name": "libvibratorservice_test",
+ "options": [
+ // TODO(b/293603710): Fix flakiness
+ {
+ "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleRunsOnlyAfterDelay"
+ },
+ // TODO(b/293623689): Fix flakiness
+ {
+ "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleMultipleCallbacksRunsInDelayOrder"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
"name": "libvibratorservice_test"
}
+ ],
+ "imports": [
+ {
+ "path": "cts/tests/vibrator"
+ }
]
}
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index aaeb8f9..4c0910a 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -102,18 +102,6 @@
ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
}
-TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
- std::vector<std::thread> threads;
- for (int i = 0; i < 5; i++) {
- threads.push_back(std::thread(
- [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- ASSERT_TRUE(waitForCallbacks(5, 25ms));
- ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
-}
-
TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
mScheduler->schedule(createCallback(1), 5ms);
mScheduler.reset(nullptr);
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 114f863..07b9569 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -532,7 +532,8 @@
return native_format;
}
-android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) {
+android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace,
+ PixelFormat pixelFormat) {
switch (colorspace) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return HAL_DATASPACE_V0_SRGB;
@@ -551,7 +552,14 @@
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
return HAL_DATASPACE_V0_SRGB;
case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
- return HAL_DATASPACE_BT2020_LINEAR;
+ if (pixelFormat == PixelFormat::RGBA_FP16) {
+ return static_cast<android_dataspace>(
+ HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_LINEAR |
+ HAL_DATASPACE_RANGE_EXTENDED);
+ } else {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ }
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
return static_cast<android_dataspace>(
HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
@@ -561,9 +569,7 @@
HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
HAL_DATASPACE_RANGE_FULL);
case VK_COLOR_SPACE_HDR10_HLG_EXT:
- return static_cast<android_dataspace>(
- HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_HLG |
- HAL_DATASPACE_RANGE_FULL);
+ return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
return static_cast<android_dataspace>(
HAL_DATASPACE_STANDARD_ADOBE_RGB |
@@ -1364,7 +1370,7 @@
PixelFormat native_pixel_format =
GetNativePixelFormat(create_info->imageFormat);
android_dataspace native_dataspace =
- GetNativeDataspace(create_info->imageColorSpace);
+ GetNativeDataspace(create_info->imageColorSpace, native_pixel_format);
if (native_dataspace == HAL_DATASPACE_UNKNOWN) {
ALOGE(
"CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "