Merge "Add component to the input tests presubmit module" into main am: 8ffdc4b563 am: c57676394a am: 503e3a6471 am: 9f028ebc9a am: a1a8c1e8f5 am: e850a1ba8a
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/2653928
Change-Id: I0ba0d2820093d1e2d6a0540b6a5e7ed215e23cbe
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.bp b/data/etc/Android.bp
index 226cae1..2143d93 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -263,6 +263,12 @@
}
prebuilt_etc {
+ name: "android.hardware.threadnetwork.prebuilt.xml",
+ src: "android.hardware.threadnetwork.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.usb.accessory.prebuilt.xml",
src: "android.hardware.usb.accessory.xml",
defaults: ["frameworks_native_data_etc_defaults"],
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/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.threadnetwork.xml
new file mode 100644
index 0000000..9cbdc90
--- /dev/null
+++ b/data/etc/android.hardware.threadnetwork.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!-- Adds the feature indicating support for the ThreadNetwork API -->
+<permissions>
+ <feature name="android.hardware.threadnetwork" />
+</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/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..a271f0c 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;
@@ -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);
@@ -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,
@@ -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/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/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/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 82e1b5a..75bae76 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",
},
}
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/input/Android.bp b/libs/input/Android.bp
index 8a17d8a..769677c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -109,62 +109,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 +118,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 +158,14 @@
"toolbox_input_labels",
],
- generated_sources: ["libinput_cxx_bridge_code"],
-
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"liblog",
"libPlatformProperties",
"libtinyxml2",
+ "libutils",
"libvintf",
],
@@ -231,15 +181,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 +202,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/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..851babf 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;
@@ -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 68e6888..80a8d03 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..87c7768 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -22,7 +22,6 @@
#include <math.h>
#include <optional>
-#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
#include <input/VelocityTracker.h>
#include <utils/BitSet.h>
@@ -56,6 +55,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
@@ -268,12 +270,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 +347,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 +372,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 +468,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 +508,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 +542,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 +564,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 +630,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 +708,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 +730,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 +770,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 +822,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 +868,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 +890,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 +980,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 +1026,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..a308c26
--- /dev/null
+++ b/libs/input/rust/input.rs
@@ -0,0 +1,126 @@
+/*
+ * 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: i32 {
+ /// FLAG_CANCELED
+ const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
+ }
+}
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..892f558
--- /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: i32,
+ ) -> 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: i32,
+) -> 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/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 18d9864..871d258 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>
@@ -1109,7 +1110,7 @@
}
if (kFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
- activeSurface->flush();
+ skgpu::ganesh::Flush(activeSurface);
}
}
for (const auto& borderRenderInfo : display.borderInfoList) {
@@ -1137,7 +1138,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/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/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/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..f749b0e 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,6 +243,7 @@
"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",
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..ca00e7d 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -116,6 +116,14 @@
]
},
{
+ "name": "CtsAppTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.cts.ToolbarActionBarTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
@@ -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..7a41083 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
}
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 3545b98..a6aa8a1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2690,9 +2690,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);
@@ -2980,7 +2977,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 +3005,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 +3299,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 +3326,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 +3341,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;
}
@@ -4094,9 +4084,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;
}
}
@@ -4354,7 +4344,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 +4503,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) {
@@ -5130,13 +5124,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 +5177,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
@@ -6725,7 +6695,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 +6784,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 +6896,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 37f569e..9cd1666 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -148,11 +148,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..e6941ef 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);
}
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index af2a3cb..e741137 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;
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..25b9643 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,24 +41,24 @@
std::set<int32_t> getActiveDeviceIds() const;
bool hasTouchingPointers(int32_t device) const;
- void removeTouchingPointer(int32_t deviceId, int32_t pointerId);
- void removeTouchingPointerFromWindow(int32_t deviceId, int32_t pointerId,
+ 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/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 49597e2..f13885a 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -226,11 +226,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/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/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 0a64a1c..2aaddf5 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,6 +222,7 @@
mAssociatedDeviceType =
getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
+ mIsWaking = mConfiguration.getBool("device.wake").value_or(false);
}
if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
@@ -376,9 +379,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/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 2f8e5bd..aae3fe7 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) {
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 7388752..e03a773 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -205,6 +205,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 +227,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 +236,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);
}
} else {
@@ -248,6 +252,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
@@ -281,9 +286,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;
}
@@ -410,7 +414,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 cd3d3c4..45fd68b 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{};
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/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 300bb85..38640b3 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -59,6 +59,7 @@
"NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
"PropertyProvider_test.cpp",
+ "SlopController_test.cpp",
"SyncQueue_test.cpp",
"TestInputListener.cpp",
"TouchpadInputMapper_test.cpp",
@@ -74,25 +75,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/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 51202b7..d367cd7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -206,11 +206,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 +347,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 +543,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 +641,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 +682,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 +718,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 +734,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 +746,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 +758,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 +770,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 +782,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 +793,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 +806,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 +818,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 +832,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))
@@ -1560,8 +1551,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 +1562,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 +1573,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 +1603,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;
}
@@ -1932,8 +1923,7 @@
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(),
@@ -3941,9 +3931,7 @@
/**
* 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>();
@@ -3973,8 +3961,6 @@
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDup}}});
- window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
-
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.downTime(baseTime + 10)
.eventTime(baseTime + 30)
@@ -3982,19 +3968,26 @@
.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)
@@ -5705,10 +5698,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();
@@ -6099,36 +6091,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 +6143,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 {
@@ -6191,7 +6183,7 @@
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();
@@ -6221,12 +6213,12 @@
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();
@@ -6614,11 +6606,11 @@
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.
@@ -6684,7 +6676,7 @@
InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not go to window because we have no focused window.
@@ -6748,7 +6740,7 @@
// 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);
+ 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);
@@ -6770,7 +6762,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;
@@ -6799,7 +6791,7 @@
// 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);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6823,7 +6815,7 @@
// 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);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 50ms);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -7039,7 +7031,7 @@
InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 50ms);
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
@@ -7144,7 +7136,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 +7145,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.
@@ -7239,20 +7231,21 @@
// 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);
+ mUnfocusedWindow->setDispatchingTimeout(
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
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 +7254,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();
}
@@ -7369,7 +7360,7 @@
InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/10ms);
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms);
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
@@ -7457,7 +7448,7 @@
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(60ms);
+ focusedApplication->setDispatchingTimeout(100ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
@@ -7470,7 +7461,7 @@
// 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,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -7481,7 +7472,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(50ms);
// Touch unfocused window. This should force the pending key to get dropped.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -9442,8 +9433,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();
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d1c3f7d..e751c89 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 {
@@ -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/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..e93b592 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -16,6 +16,8 @@
#include <CursorInputMapper.h>
#include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
namespace android {
@@ -38,7 +40,7 @@
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
+ InputReaderConfiguration policyConfig;
CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>(policyConfig);
// Loop through mapper operations until randomness is exhausted.
@@ -65,22 +67,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
index b992928..ade5328 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -25,11 +25,9 @@
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:
@@ -38,12 +36,12 @@
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);
+ 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,
+ sp<FuzzInputReaderPolicy> policy = sp<FuzzInputReaderPolicy>::make(mFdp);
+ mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, policy,
mFuzzListener, mFdp);
InputDeviceIdentifier identifier;
@@ -51,7 +49,6 @@
identifier.location = deviceLocation;
mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
identifier);
- mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
}
~FuzzContainer() {}
@@ -59,7 +56,7 @@
void configureDevice() {
nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
std::list<NotifyArgs> out;
- out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, /*changes=*/{});
+ out += mFuzzDevice->configure(arbitraryTime, /*readerConfig=*/{}, /*changes=*/{});
out += mFuzzDevice->reset(arbitraryTime);
for (const NotifyArgs& args : out) {
mFuzzListener.notify(args);
@@ -71,7 +68,9 @@
configureDevice();
}
- InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
+ void setAbsoluteAxisInfo(int axis, const RawAbsoluteAxisInfo& axisInfo) {
+ mFuzzEventHub->setAbsoluteAxisInfo(mFuzzDevice->getId(), axis, axisInfo);
+ }
template <class T, typename... Args>
T& getMapper(Args... args) {
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 616e870..54977df 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -15,7 +15,9 @@
*/
#include <FuzzContainer.h>
+#include <InputReaderBase.h>
#include <KeyboardInputMapper.h>
+#include <MapperHelpers.h>
namespace android {
@@ -44,9 +46,9 @@
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
KeyboardInputMapper& mapper =
- fuzzer.getMapper<KeyboardInputMapper>(policyConfig, fdp->ConsumeIntegral<uint32_t>(),
+ fuzzer.getMapper<KeyboardInputMapper>(InputReaderConfiguration{},
+ fdp->ConsumeIntegral<uint32_t>(),
fdp->ConsumeIntegral<int32_t>());
// Loop through mapper operations until randomness is exhausted.
@@ -64,7 +66,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 +74,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 1e44e0f..5039d1a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -15,6 +15,9 @@
*/
#pragma once
+#include <map>
+
+#include <EventHub.h>
#include <InputDevice.h>
#include <InputMapper.h>
#include <InputReader.h>
@@ -22,7 +25,6 @@
constexpr size_t kValidTypes[] = {EV_SW,
EV_SYN,
- SYN_REPORT,
EV_ABS,
EV_KEY,
EV_MSC,
@@ -46,7 +48,6 @@
ABS_MT_PRESSURE,
ABS_MT_DISTANCE,
ABS_MT_TOOL_TYPE,
- SYN_MT_REPORT,
MSC_SCAN,
REL_X,
REL_Y,
@@ -74,10 +75,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 +115,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 +146,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;
}
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 212462d..569767f 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -15,6 +15,8 @@
*/
#include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
#include <MultiTouchInputMapper.h>
namespace android {
@@ -61,7 +63,7 @@
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
+ InputReaderConfiguration policyConfig;
MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>(policyConfig);
// Loop through mapper operations until randomness is exhausted.
@@ -87,16 +89,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..80eebd5 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -15,6 +15,8 @@
*/
#include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
#include <SwitchInputMapper.h>
namespace android {
@@ -24,8 +26,7 @@
std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
- auto policyConfig = fuzzer.getPolicyConfig();
- SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(policyConfig);
+ SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>(InputReaderConfiguration{});
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
@@ -36,16 +37,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..796178a
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 <string>
+#include <vector>
+
+#include <linux/input-event-codes.h>
+
+#include <FuzzContainer.h>
+#include <InputReaderBase.h>
+#include <MapperHelpers.h>
+#include <TouchpadInputMapper.h>
+
+namespace android {
+
+namespace {
+
+void setAxisInfo(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer, int axis) {
+ if (fdp.ConsumeBool()) {
+ fuzzer.setAbsoluteAxisInfo(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, FuzzContainer& fuzzer) {
+ setAxisInfo(fdp, fuzzer, ABS_MT_SLOT);
+ setAxisInfo(fdp, fuzzer, ABS_MT_POSITION_X);
+ setAxisInfo(fdp, fuzzer, ABS_MT_POSITION_Y);
+ setAxisInfo(fdp, fuzzer, ABS_MT_PRESSURE);
+ setAxisInfo(fdp, fuzzer, ABS_MT_ORIENTATION);
+ setAxisInfo(fdp, fuzzer, ABS_MT_TOUCH_MAJOR);
+ setAxisInfo(fdp, fuzzer, ABS_MT_TOUCH_MINOR);
+ setAxisInfo(fdp, fuzzer, ABS_MT_WIDTH_MAJOR);
+ setAxisInfo(fdp, fuzzer, 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, FuzzContainer& fuzzer) {
+ // 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()) {
+ fuzzer.addProperty("gestureProp.Touchpad_Stack_Version",
+ std::to_string(fdp.ConsumeIntegral<int>()));
+ }
+
+ for (auto& propertyName : boolPropertiesToFuzz) {
+ if (fdp.ConsumeBool()) {
+ fuzzer.addProperty(propertyName, fdp.ConsumeBool() ? "1" : "0");
+ }
+ }
+
+ for (auto& propertyName : doublePropertiesToFuzz) {
+ if (fdp.ConsumeBool()) {
+ fuzzer.addProperty(propertyName, std::to_string(fdp.ConsumeFloatingPoint<double>()));
+ }
+ }
+
+ if (fdp.ConsumeBool()) {
+ fuzzer.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);
+ FuzzContainer fuzzer(fdp);
+ setAxisInfos(*fdp, fuzzer);
+ setDeviceSpecificConfig(*fdp, fuzzer);
+
+ 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 = fuzzer.getMapper<TouchpadInputMapper>(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/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5683a92..89c80bc 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
@@ -213,14 +214,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 +227,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/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..1416872 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -67,6 +67,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,7 +94,7 @@
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;
@@ -145,7 +146,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..3a19d0b 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"
@@ -819,6 +820,13 @@
: parentSnapshot.frameRate;
}
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
+ snapshot.frameRateSelectionPriority =
+ (requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET)
+ ? parentSnapshot.frameRateSelectionPriority
+ : requested.frameRateSelectionPriority;
+ }
+
if (forceUpdate ||
snapshot.clientChanges &
(layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
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/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..607bec2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -148,7 +148,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);
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 f136e9f..7951c33 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -853,6 +853,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 41639b6..0a1bd4c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -260,29 +260,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) {
@@ -555,6 +566,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()) {
@@ -571,7 +587,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;
@@ -579,6 +597,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() {
@@ -822,6 +854,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();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 17e9cea..c44c447 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();
@@ -310,9 +314,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);
@@ -386,6 +387,12 @@
GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
bool updateFrameRateOverrides(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);
@@ -506,6 +513,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/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 8ffc3c3..2782ce4 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>
@@ -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);
@@ -493,10 +497,6 @@
return LatchUnsignaledConfig::AutoSingleLayer;
}
- if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
- return LatchUnsignaledConfig::Always;
- }
-
return LatchUnsignaledConfig::Disabled;
}
@@ -690,7 +690,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 +704,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 +803,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();
@@ -1945,8 +1946,21 @@
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));
@@ -2020,7 +2034,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 +2183,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 +2246,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 +2291,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 +2325,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;
@@ -2414,25 +2474,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()) {
@@ -2455,8 +2506,14 @@
// 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();
}
@@ -2859,31 +2916,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);
}
}
@@ -3854,6 +3935,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;
@@ -4236,18 +4325,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
@@ -4255,7 +4342,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
@@ -4292,18 +4379,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());
@@ -4328,15 +4414,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);
}
@@ -4389,18 +4578,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) {
@@ -4427,7 +4611,7 @@
}
}
- return layer->isSimpleBufferUpdate(state);
+ return true;
}
status_t SurfaceFlinger::setTransactionState(
@@ -5557,7 +5741,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)},
@@ -5575,17 +5759,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();
@@ -5915,7 +6138,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) {
@@ -5927,7 +6150,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");
}
}
@@ -5976,7 +6225,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);
@@ -6049,7 +6301,9 @@
}
result.push_back('\n');
- dumpHwcLayersMinidumpLocked(result);
+ if (mLegacyFrontEndEnabled) {
+ dumpHwcLayersMinidumpLockedLegacy(result);
+ }
{
DumpArgs plannerArgs;
@@ -7703,6 +7957,7 @@
if (mTransactionTracing) {
mTransactionTracing->onLayerRemoved(layer->getSequence());
}
+ mScheduler->onLayerDestroyed(layer);
}
void SurfaceFlinger::onLayerUpdate() {
@@ -7798,7 +8053,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 {
@@ -8272,6 +8527,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 e27d21f..1947072 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;
@@ -642,6 +646,7 @@
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
void triggerOnFrameRateOverridesChanged() override;
+ void onChoreographerAttached() override;
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
@@ -714,10 +719,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);
@@ -761,6 +765,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);
@@ -785,8 +792,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);
@@ -1044,7 +1050,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;
@@ -1175,6 +1182,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;
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/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/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/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_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 0c9a16b..00e92a1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -792,6 +792,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..5cac086 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -472,6 +472,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 +494,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/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/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/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/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 4301186..3234483 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -308,6 +308,17 @@
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);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 5da893e..d6c4b72 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,48 @@
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);
+}
+
} // 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..405f79d 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;
@@ -199,7 +208,8 @@
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
- mScheduler->chooseRefreshRateForContent();
+ mScheduler->chooseRefreshRateForContent(/*LayerHierarchy*/ nullptr,
+ /*updateAttachedChoreographer*/ false);
}
TEST_F(SchedulerTest, updateDisplayModes) {
@@ -249,6 +259,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 +288,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 +449,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_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 aac11c0..fa7a947 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 833984f..5be4548 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -622,6 +622,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() {
@@ -632,6 +636,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>();
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/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/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/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);