Merge changes Ie9d7e9a3,I88d60484 into main
* changes:
Migrate legacy framerate tests
Fix framerate propagation from child to parent layers
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs
index db3fd77..e91e5da 100644
--- a/cmds/evemu-record/main.rs
+++ b/cmds/evemu-record/main.rs
@@ -50,8 +50,10 @@
/// The first event received from the device.
FirstEvent,
- /// The time when the system booted.
- Boot,
+ /// The Unix epoch (00:00:00 UTC on 1st January 1970), so that all timestamps are Unix
+ /// timestamps. This makes the events in the recording easier to match up with those from other
+ /// log sources.
+ Epoch,
}
fn get_choice(max: u32) -> u32 {
@@ -188,7 +190,7 @@
//
// [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1),
- TimestampBase::Boot => TimeVal::new(0, 0),
+ TimestampBase::Epoch => TimeVal::new(0, 0),
};
print_event(output, &event.offset_time_by(start_time))?;
loop {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index ef2fa4d..fa7cb64 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -505,8 +505,9 @@
return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services.");
}
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
if (binder == nullptr) {
@@ -888,8 +889,9 @@
}
auto ctx = mAccess->getCallingContext();
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
auto serviceIt = mNameToService.find(name);
@@ -1051,8 +1053,9 @@
}
auto ctx = mAccess->getCallingContext();
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
auto serviceIt = mNameToService.find(name);
@@ -1110,6 +1113,23 @@
return Status::ok();
}
+Status ServiceManager::canAddService(const Access::CallingContext& ctx, const std::string& name,
+ std::optional<std::string>* accessor) {
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied for service.");
+ }
+#ifndef VENDORSERVICEMANAGER
+ *accessor = getVintfAccessorName(name);
+#endif
+ if (accessor->has_value()) {
+ if (!mAccess->canAdd(ctx, accessor->value())) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "SELinux denied for the accessor of the service.");
+ }
+ }
+ return Status::ok();
+}
+
Status ServiceManager::canFindService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor) {
if (!mAccess->canFind(ctx, name)) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 0d666c6..c92141b 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -115,6 +115,8 @@
os::Service tryGetService(const std::string& name, bool startIfNotFound);
sp<IBinder> tryGetBinder(const std::string& name, bool startIfNotFound);
+ binder::Status canAddService(const Access::CallingContext& ctx, const std::string& name,
+ std::optional<std::string>* accessor);
binder::Status canFindService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor);
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index 14540ec..f593eda 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -38,7 +38,8 @@
<low-jerk>1.5</low-jerk>
<high-jerk>2.0</high-jerk>
- <!-- The forget factor in the first-order IIR filter for jerk smoothing -->
- <jerk-forget-factor>0.25</jerk-forget-factor>
+ <!-- The alpha in the first-order IIR filter for jerk smoothing. An alpha
+ of 1 results in no smoothing.-->
+ <jerk-alpha>0.25</jerk-alpha>
</motion-predictor>
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 97e4dc0..3f32a5a 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -53,6 +53,7 @@
*/
#include <android/api-level.h>
+#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
@@ -84,7 +85,6 @@
/**
* An opaque type representing a handle to a performance hint manager.
- * It must be released after use.
*
* To use:<ul>
* <li>Obtain the performance hint manager instance by calling
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 099a2bc..82cacca 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -373,6 +373,28 @@
float b, float alpha, enum ADataSpace dataspace)
__INTRODUCED_IN(29);
+// These APIs (setGeometry and setCrop) were originally written in a
+// C-incompatible form using references instead of pointers, and the OS shipped
+// that version for years before it was noticed. Fortunately the compiled code
+// for callers is the same regardless of whether it's a pointer or a reference,
+// so we can declare this as a nonnull pointer for C and keep the existing C++
+// decl and definition.
+//
+// We could alternatively change the decl and the definition to both be a
+// pointer (with an inline definition using references to preserve source compat
+// for existing C++ callers), but that requires changing the definition of an
+// API that has been in the OS for years. It's theoretically a safe change, but
+// without being able to prove it that's a very big risk to take. By keeping the
+// C-compatibility hack in the header, we can be sure that we haven't changed
+// anything for existing callers. By definition there were no C users of the
+// reference-based decl; if there were any C callers of the API at all, they were
+// using the same workaround that is now used below.
+//
+// Even if this workaround turns out to not work for C, there's no permanent
+// damage done to the platform (unlike if we were to change the definition). At
+// worst it continues to work for C++ (since the preprocessed header as seen by
+// C++ hasn't changed, nor has the definition) and continues to not work for C.
+
/**
* \param source The sub-rect within the buffer's content to be rendered inside the surface's area
* The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
@@ -383,7 +405,7 @@
* clipped by the bounds of its parent. The destination rect's width and height must be > 0.
*
* \param transform The transform applied after the source rect is applied to the buffer. This
- * parameter should be set to 0 for no transform. To specify a transfrom use the
+ * parameter should be set to 0 for no transform. To specify a transform use the
* NATIVE_WINDOW_TRANSFORM_* enum.
*
* Available since API level 29.
@@ -394,9 +416,14 @@
* properties at once.
*/
void ASurfaceTransaction_setGeometry(ASurfaceTransaction* _Nonnull transaction,
- ASurfaceControl* _Nonnull surface_control, const ARect& source,
- const ARect& destination, int32_t transform)
- __INTRODUCED_IN(29);
+ ASurfaceControl* _Nonnull surface_control,
+#if defined(__cplusplus)
+ const ARect& source, const ARect& destination,
+#else
+ const ARect* _Nonnull source,
+ const ARect* _Nonnull destination,
+#endif
+ int32_t transform) __INTRODUCED_IN(29);
/**
* Bounds the surface and its children to the bounds specified. The crop and buffer size will be
@@ -408,7 +435,12 @@
* Available since API level 31.
*/
void ASurfaceTransaction_setCrop(ASurfaceTransaction* _Nonnull transaction,
- ASurfaceControl* _Nonnull surface_control, const ARect& crop)
+ ASurfaceControl* _Nonnull surface_control,
+#if defined(__cplusplus)
+ const ARect& crop)
+#else
+ const ARect* _Nonnull crop)
+#endif
__INTRODUCED_IN(31);
/**
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
index bdc5249..f0503f6 100644
--- a/include/android/surface_control_input_receiver.h
+++ b/include/android/surface_control_input_receiver.h
@@ -59,17 +59,13 @@
AInputEvent *_Nonnull keyEvent)
__INTRODUCED_IN(__ANDROID_API_V__);
-struct AInputReceiverCallbacks;
-
-struct AInputReceiver;
+typedef struct AInputReceiverCallbacks AInputReceiverCallbacks;
/**
* The InputReceiver that holds the reference to the registered input channel. This must be released
* using AInputReceiver_release
- *
- * Available since API level 35.
*/
-typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__);
+typedef struct AInputReceiver AInputReceiver;
/**
* Registers an input receiver for an ASurfaceControl that will receive batched input event. For
diff --git a/include/android/thermal.h b/include/android/thermal.h
index fa168cd..7f9d2ed 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -85,6 +85,7 @@
/** Need shutdown immediately. */
ATHERMAL_STATUS_SHUTDOWN = 6,
};
+typedef enum AThermalStatus AThermalStatus;
/**
* An opaque type representing a handle to a thermal manager.
@@ -240,6 +241,7 @@
float headroom;
AThermalStatus thermalStatus;
};
+typedef struct AThermalHeadroomThreshold AThermalHeadroomThreshold;
/**
* Gets the thermal headroom thresholds for all available thermal status.
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index ae8de5f..65c2914 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -17,6 +17,7 @@
#pragma once
#include <input/InputTransport.h>
+#include <input/Resampler.h>
#include <utils/Looper.h>
namespace android {
@@ -47,13 +48,13 @@
/**
* Consumes input events from an input channel.
*
- * This is a re-implementation of InputConsumer that does not have resampling at the current moment.
- * A lot of the higher-level logic has been folded into this class, to make it easier to use.
- * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer,
- * as well as various actions like adding the fd to the Choreographer.
+ * This is a re-implementation of InputConsumer. At the moment it only supports resampling for
+ * single pointer events. A lot of the higher-level logic has been folded into this class, to make
+ * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled
+ * in the jni layer, as well as various actions like adding the fd to the Choreographer.
*
* TODO(b/297226446): use this instead of "InputConsumer":
- * - Add resampling to this class
+ * - Add resampling for multiple pointer events.
* - Allow various resampling strategies to be specified
* - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer".
* - Add tracing
@@ -64,8 +65,18 @@
*/
class InputConsumerNoResampling final {
public:
+ /**
+ * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever
+ * the event is ready to consume.
+ * @param looper needs to be sp and not shared_ptr because it inherits from
+ * RefBase
+ * @param resampler the resampling strategy to use. If null, no resampling will be
+ * performed.
+ */
explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper, InputConsumerCallbacks& callbacks);
+ sp<Looper> looper, InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler);
+
~InputConsumerNoResampling();
/**
@@ -99,6 +110,7 @@
std::shared_ptr<InputChannel> mChannel;
sp<Looper> mLooper;
InputConsumerCallbacks& mCallbacks;
+ std::unique_ptr<Resampler> mResampler;
// Looper-related infrastructure
/**
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 25d35e9..55e0583 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -127,7 +127,7 @@
return *this;
}
- MotionEvent build() {
+ MotionEvent build() const {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
for (const PointerBuilder& pointer : mPointers) {
@@ -135,20 +135,22 @@
pointerCoords.push_back(pointer.buildCoords());
}
+ auto [xCursorPosition, yCursorPosition] =
+ std::make_pair(mRawXCursorPosition, mRawYCursorPosition);
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
+ !MotionEvent::isValidCursorPosition(xCursorPosition, yCursorPosition)) {
+ xCursorPosition = pointerCoords[0].getX();
+ yCursorPosition = pointerCoords[0].getY();
}
MotionEvent event;
event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
MotionClassification::NONE, mTransform,
- /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, mRawTransform, mDownTime, mEventTime,
- mPointers.size(), pointerProperties.data(), pointerCoords.data());
+ /*xPrecision=*/0, /*yPrecision=*/0, xCursorPosition, yCursorPosition,
+ mRawTransform, mDownTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
return event;
}
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 2f1ef86..200c301 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -43,7 +43,9 @@
class JerkTracker {
public:
// Initialize the tracker. If normalizedDt is true, assume that each sample pushed has dt=1.
- JerkTracker(bool normalizedDt);
+ // alpha is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
+ // in no smoothing.
+ JerkTracker(bool normalizedDt, float alpha);
// Add a position to the tracker and update derivative estimates.
void pushSample(int64_t timestamp, float xPos, float yPos);
@@ -56,15 +58,10 @@
// acceleration) and has the units of d^3p/dt^3.
std::optional<float> jerkMagnitude() const;
- // forgetFactor is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
- // in no smoothing.
- void setForgetFactor(float forgetFactor);
- float getForgetFactor() const;
-
private:
const bool mNormalizedDt;
// Coefficient of first-order IIR filter to smooth jerk calculation.
- float mForgetFactor = 1;
+ const float mAlpha;
RingBuffer<int64_t> mTimestamps{4};
std::array<float, 4> mXDerivatives{}; // [x, x', x'', x''']
@@ -124,11 +121,6 @@
bool isPredictionAvailable(int32_t deviceId, int32_t source);
- /**
- * Currently used to expose config constants in testing.
- */
- const TfLiteMotionPredictorModel::Config& getModelConfig();
-
private:
const nsecs_t mPredictionTimestampOffsetNanos;
const std::function<bool()> mCheckMotionPredictionEnabled;
@@ -137,15 +129,17 @@
std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
std::optional<MotionEvent> mLastEvent;
- // mJerkTracker assumes normalized dt = 1 between recorded samples because
- // the underlying mModel input also assumes fixed-interval samples.
- // Normalized dt as 1 is also used to correspond with the similar Jank
- // implementation from the JetPack MotionPredictor implementation.
- JerkTracker mJerkTracker{true};
- std::optional<MotionPredictorMetricsManager> mMetricsManager;
+ std::unique_ptr<JerkTracker> mJerkTracker;
+
+ std::unique_ptr<MotionPredictorMetricsManager> mMetricsManager;
const ReportAtomFunction mReportAtomFunction;
+
+ // Initialize prediction model and associated objects.
+ // Called during lazy initialization.
+ // TODO: b/210158587 Consider removing lazy initialization.
+ void initializeObjects();
};
} // namespace android
diff --git a/include/input/OWNERS b/include/input/OWNERS
index c88bfe9..21d208f 100644
--- a/include/input/OWNERS
+++ b/include/input/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
include platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
new file mode 100644
index 0000000..ff9c4b0
--- /dev/null
+++ b/include/input/Resampler.h
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <optional>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/RingBuffer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Resampler is an interface for resampling MotionEvents. Every resampling implementation
+ * must use this interface to enable resampling inside InputConsumer's logic.
+ */
+struct Resampler {
+ virtual ~Resampler() = default;
+
+ /**
+ * Tries to resample motionEvent at resampleTime. The provided resampleTime must be greater than
+ * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
+ * resampleTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
+ * may be resampled by another method, or not resampled at all. Furthermore, it is the
+ * implementer's responsibility to guarantee the following:
+ * - If resampling occurs, a single additional sample should be added to motionEvent. That is,
+ * if motionEvent had N samples before being passed to Resampler, then it will have N + 1
+ * samples by the end of the resampling. No other field of motionEvent should be modified.
+ * - If resampling does not occur, then motionEvent must not be modified in any way.
+ */
+ virtual void resampleMotionEvent(const std::chrono::nanoseconds resampleTime,
+ MotionEvent& motionEvent,
+ const InputMessage* futureSample) = 0;
+};
+
+class LegacyResampler final : public Resampler {
+public:
+ /**
+ * Tries to resample `motionEvent` at `resampleTime` by adding a resampled sample at the end of
+ * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by
+ * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if
+ * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
+ * not null, interpolation will occur. If `futureSample` is null and there is enough historical
+ * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
+ * `motionEvent` is unmodified.
+ */
+ void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) override;
+
+private:
+ struct Pointer {
+ PointerProperties properties;
+ PointerCoords coords;
+ };
+
+ struct Sample {
+ std::chrono::nanoseconds eventTime;
+ Pointer pointer;
+
+ Sample(const std::chrono::nanoseconds eventTime, const PointerProperties& properties,
+ const PointerCoords& coords)
+ : eventTime{eventTime}, pointer{properties, coords} {}
+ };
+
+ /**
+ * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous
+ * and the current deviceId.
+ */
+ std::optional<DeviceId> mPreviousDeviceId;
+
+ /**
+ * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called.
+ * Note: We store up to two samples in order to simplify the implementation. Although,
+ * calculations are possible with only one previous sample.
+ */
+ RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
+
+ /**
+ * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. (If
+ * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
+ * added to mLatestSamples.)
+ */
+ void updateLatestSamples(const MotionEvent& motionEvent);
+
+ /**
+ * May add a sample at the end of motionEvent with eventTime equal to resampleTime, and
+ * interpolated coordinates between the latest motionEvent sample and futureSample.
+ */
+ void interpolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage& futureSample) const;
+
+ /**
+ * May add a sample at the end of motionEvent by extrapolating from the latest two samples. The
+ * added sample either has eventTime equal to resampleTime, or an earlier time if resampleTime
+ * is too far in the future.
+ */
+ void extrapolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent) const;
+};
+} // namespace android
\ No newline at end of file
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 08a4330..49e909e 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -112,7 +112,7 @@
float highJerk = 0;
// Coefficient for the first-order IIR filter for jerk calculation.
- float jerkForgetFactor = 1;
+ float jerkAlpha = 1;
};
// Creates a model from an encoded Flatbuffer model.
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index dabe45c..b6c6305 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -17,14 +17,30 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <input/Input.h>
+#include <map>
namespace android {
+enum class DeviceType {
+ KEYBOARD,
+ MOUSE,
+ TOUCHSCREEN,
+ DPAD,
+ STYLUS,
+ ROTARY_ENCODER,
+};
+
+android::base::unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth);
+
enum class UinputAction {
RELEASE = 0,
PRESS = 1,
MOVE = 2,
CANCEL = 3,
+ ftl_last = CANCEL,
};
class VirtualInputDevice {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index c57c9cd..53bd08d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -143,6 +143,22 @@
return reply.readNullableStrongBinder(out);
}
+status_t IBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->addFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
+status_t IBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->removeFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
status_t IBinder::getDebugPid(pid_t* out) {
BBinder* local = this->localBinder();
if (local != nullptr) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 6594aa6..eae844c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -160,11 +160,12 @@
// ---------------------------------------------------------------------------
-sp<BpBinder> BpBinder::create(int32_t handle) {
+sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) {
if constexpr (!kEnableKernelIpc) {
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
return nullptr;
}
+ LOG_ALWAYS_FATAL_IF(postTask == nullptr, "BAD STATE");
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
@@ -183,7 +184,11 @@
ALOGE("Still too many binder proxy objects sent to uid %d from uid %d (%d proxies "
"held)",
getuid(), trackedUid, trackedValue);
- if (sLimitCallback) sLimitCallback(trackedUid);
+
+ if (sLimitCallback) {
+ *postTask = [=]() { sLimitCallback(trackedUid); };
+ }
+
sLastLimitCallbackMap[trackedUid] = trackedValue;
}
} else {
@@ -197,7 +202,11 @@
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
- if (sLimitCallback) sLimitCallback(trackedUid);
+
+ if (sLimitCallback) {
+ *postTask = [=]() { sLimitCallback(trackedUid); };
+ }
+
sLastLimitCallbackMap[trackedUid] = trackedValue & COUNTING_VALUE_MASK;
if (sBinderProxyThrottleCreate) {
ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
@@ -557,6 +566,123 @@
}
}
+status_t BpBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "addFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+ LOG_ALWAYS_FATAL_IF(ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0,
+ "addFrozenStateChangeCallback on %s but there are no threads "
+ "(yet?) listening to incoming transactions. See "
+ "ProcessState::startThreadPool "
+ "and ProcessState::setThreadPoolMaxThreadCount. Generally you should "
+ "setup the binder threadpool before other initialization steps.",
+ String8(getInterfaceDescriptor()).c_str());
+ LOG_ALWAYS_FATAL_IF(callback == nullptr,
+ "addFrozenStateChangeCallback(): callback must be non-NULL");
+
+ const sp<FrozenStateChangeCallback> strongCallback = callback.promote();
+ if (strongCallback == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ ALOGV("Requesting freeze notification: %p handle %d\n", this, binderHandle());
+ IPCThreadState* self = IPCThreadState::self();
+ status_t status = self->addFrozenStateChangeCallback(binderHandle(), this);
+ if (status != NO_ERROR) {
+ // Avoids logspam if kernel does not support freeze
+ // notification.
+ if (status != INVALID_OPERATION) {
+ ALOGE("IPCThreadState.addFrozenStateChangeCallback "
+ "failed with %s. %p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ return status;
+ }
+ mFrozen = std::make_unique<FrozenStateChange>();
+ if (!mFrozen) {
+ std::ignore =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ return NO_MEMORY;
+ }
+ }
+ if (mFrozen->initialStateReceived) {
+ strongCallback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ mFrozen->isFrozen
+ ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ }
+ ssize_t res = mFrozen->callbacks.add(callback);
+ if (res < 0) {
+ return res;
+ }
+ return NO_ERROR;
+ }
+}
+
+status_t BpBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "removeFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ RpcMutexUniqueLock _l(mLock);
+
+ const size_t N = mFrozen ? mFrozen->callbacks.size() : 0;
+ for (size_t i = 0; i < N; i++) {
+ if (mFrozen->callbacks.itemAt(i) == callback) {
+ mFrozen->callbacks.removeAt(i);
+ if (mFrozen->callbacks.size() == 0) {
+ ALOGV("Clearing freeze notification: %p handle %d\n", this, binderHandle());
+ status_t status =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ if (status != NO_ERROR) {
+ ALOGE("Unexpected error from "
+ "IPCThreadState.removeFrozenStateChangeCallback: %s. "
+ "%p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ mFrozen.reset();
+ }
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+void BpBinder::onFrozenStateChanged(bool isFrozen) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(), "onFrozenStateChanged is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ ALOGV("Sending frozen state change notification for proxy %p handle %d, isFrozen=%s\n", this,
+ binderHandle(), isFrozen ? "true" : "false");
+
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ return;
+ }
+ bool stateChanged = !mFrozen->initialStateReceived || mFrozen->isFrozen != isFrozen;
+ if (stateChanged) {
+ mFrozen->isFrozen = isFrozen;
+ mFrozen->initialStateReceived = true;
+ for (size_t i = 0; i < mFrozen->callbacks.size();) {
+ sp<FrozenStateChangeCallback> callback = mFrozen->callbacks.itemAt(i).promote();
+ if (callback != nullptr) {
+ callback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ isFrozen ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ i++;
+ } else {
+ mFrozen->callbacks.removeItemsAt(i);
+ }
+ }
+ }
+}
+
void BpBinder::reportOneDeath(const Obituary& obit)
{
sp<DeathRecipient> recipient = obit.recipient.promote();
@@ -686,6 +812,10 @@
if (ipc) ipc->clearDeathNotification(binderHandle(), this);
mObituaries = nullptr;
}
+ if (mFrozen != nullptr) {
+ std::ignore = IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), this);
+ mFrozen.reset();
+ }
mLock.unlock();
if (obits != nullptr) {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 984c93d..1d26d85 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -89,26 +89,33 @@
"BR_FROZEN_REPLY",
"BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_PENDING_FROZEN",
+ "BR_FROZEN_BINDER",
+ "BR_CLEAR_FREEZE_NOTIFICATION_DONE",
};
-static const char *kCommandStrings[] = {
- "BC_TRANSACTION",
- "BC_REPLY",
- "BC_ACQUIRE_RESULT",
- "BC_FREE_BUFFER",
- "BC_INCREFS",
- "BC_ACQUIRE",
- "BC_RELEASE",
- "BC_DECREFS",
- "BC_INCREFS_DONE",
- "BC_ACQUIRE_DONE",
- "BC_ATTEMPT_ACQUIRE",
- "BC_REGISTER_LOOPER",
- "BC_ENTER_LOOPER",
- "BC_EXIT_LOOPER",
- "BC_REQUEST_DEATH_NOTIFICATION",
- "BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+static const char* kCommandStrings[] = {
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
+ "BC_REQUEST_FREEZE_NOTIFICATION",
+ "BC_CLEAR_FREEZE_NOTIFICATION",
+ "BC_FREEZE_NOTIFICATION_DONE",
};
static const int64_t kWorkSourcePropagatedBitIndex = 32;
@@ -203,6 +210,18 @@
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BR_FROZEN_BINDER: {
+ const int32_t c = *cmd++;
+ const int32_t h = *cmd++;
+ const int32_t isFrozen = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c << " isFrozen: " << isFrozen;
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BR_OK, BR_DEAD_REPLY,
// BR_TRANSACTION_COMPLETE, BR_FINISHED
@@ -270,11 +289,23 @@
out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")";
} break;
+ case BC_REQUEST_FREEZE_NOTIFICATION:
+ case BC_CLEAR_FREEZE_NOTIFICATION: {
+ const int32_t h = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": handle=" << h << " (freeze cookie " << (void*)(uint64_t)c << ")";
+ } break;
+
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BC_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER,
// BC_EXIT_LOOPER
@@ -953,6 +984,33 @@
return NO_ERROR;
}
+status_t IPCThreadState::addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ proxy->getWeakRefs()->incWeak(proxy);
+ mOut.writeInt32(BC_REQUEST_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
+status_t IPCThreadState::removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ mOut.writeInt32(BC_CLEAR_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mServingStackPointer(nullptr),
@@ -1487,6 +1545,26 @@
proxy->getWeakRefs()->decWeak(proxy);
} break;
+ case BR_FROZEN_BINDER: {
+ const struct binder_frozen_state_info* data =
+ reinterpret_cast<const struct binder_frozen_state_info*>(
+ mIn.readInplace(sizeof(struct binder_frozen_state_info)));
+ if (data == nullptr) {
+ result = UNKNOWN_ERROR;
+ break;
+ }
+ BpBinder* proxy = (BpBinder*)data->cookie;
+ bool isFrozen = mIn.readInt32() > 0;
+ proxy->getPrivateAccessor().onFrozenStateChanged(data->is_frozen);
+ mOut.writeInt32(BC_FREEZE_NOTIFICATION_DONE);
+ mOut.writePointer(data->cookie);
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ BpBinder* proxy = (BpBinder*)mIn.readPointer();
+ proxy->getWeakRefs()->decWeak(proxy);
+ } break;
+
case BR_FINISHED:
result = TIMED_OUT;
break;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 8b80aed..333f956 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -253,8 +253,11 @@
}
}
+#endif //__ANDROID_VNDK__
+
void* openDeclaredPassthroughHal(const String16& interface, const String16& instance, int flag) {
-#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) && !defined(__ANDROID_NATIVE_BRIDGE__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VENDOR__) && !defined(__ANDROID_RECOVERY__) && \
+ !defined(__ANDROID_NATIVE_BRIDGE__)
sp<IServiceManager> sm = defaultServiceManager();
String16 name = interface + String16("/") + instance;
if (!sm->isDeclared(name)) {
@@ -274,8 +277,6 @@
#endif
}
-#endif //__ANDROID_VNDK__
-
// ----------------------------------------------------------------------
ServiceManagerShim::ServiceManagerShim(const sp<AidlServiceManager>& impl) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a42ede2..5e7f151 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -24,7 +24,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Stability.h>
-#include <cutils/atomic.h>
#include <utils/AndroidThreads.h>
#include <utils/String8.h>
#include <utils/Thread.h>
@@ -57,6 +56,25 @@
// -------------------------------------------------------------------------
+namespace {
+bool readDriverFeatureFile(const char* filename) {
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
+ char on;
+ if (fd == -1) {
+ ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, filename, strerror(errno));
+ return false;
+ }
+ if (read(fd, &on, sizeof(on)) == -1) {
+ ALOGE("%s: error reading to %s: %s", __func__, filename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return on == '1';
+}
+
+} // namespace
+
namespace android {
using namespace android::binder::impl;
@@ -311,6 +329,7 @@
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
+ std::function<void()> postTask;
std::unique_lock<std::mutex> _l(mLock);
@@ -358,7 +377,7 @@
return nullptr;
}
- sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
+ sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle, &postTask);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
@@ -371,6 +390,10 @@
}
}
+ _l.unlock();
+
+ if (postTask) postTask();
+
return result;
}
@@ -387,7 +410,7 @@
}
String8 ProcessState::makeBinderThreadName() {
- int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ int32_t s = mThreadPoolSeq.fetch_add(1, std::memory_order_release);
pid_t pid = getpid();
std::string_view driverName = mDriverName.c_str();
@@ -429,8 +452,17 @@
}
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
+ // Need to read `mKernelStartedThreads` before `mThreadPoolStarted` (with
+ // non-relaxed memory ordering) to avoid a race like the following:
+ //
+ // thread A: if (mThreadPoolStarted) { // evaluates false
+ // thread B: mThreadPoolStarted = true;
+ // thread B: mKernelStartedThreads++;
+ // thread A: size_t kernelStarted = mKernelStartedThreads;
+ // thread A: LOG_ALWAYS_FATAL_IF(kernelStarted != 0, ...);
+ size_t kernelStarted = mKernelStartedThreads;
+
if (mThreadPoolStarted) {
- size_t kernelStarted = mKernelStartedThreads;
size_t max = mMaxThreads;
size_t current = mCurrentThreads;
@@ -460,7 +492,6 @@
// must not be initialized or maybe has poll thread setup, we
// currently don't track this in libbinder
- size_t kernelStarted = mKernelStartedThreads;
LOG_ALWAYS_FATAL_IF(kernelStarted != 0, "Expecting 0 kernel started threads but have %zu",
kernelStarted);
return mCurrentThreads;
@@ -472,27 +503,20 @@
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
- static const char* const names[] = {
- [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
- DRIVER_FEATURES_PATH "oneway_spam_detection",
- [static_cast<int>(DriverFeature::EXTENDED_ERROR)] =
- DRIVER_FEATURES_PATH "extended_error",
- };
- int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
- char on;
- if (fd == -1) {
- ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- return false;
+ // Use static variable to cache the results.
+ if (feature == DriverFeature::ONEWAY_SPAM_DETECTION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "oneway_spam_detection");
+ return enabled;
}
- if (read(fd, &on, sizeof(on)) == -1) {
- ALOGE("%s: error reading to %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- close(fd);
- return false;
+ if (feature == DriverFeature::EXTENDED_ERROR) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "extended_error");
+ return enabled;
}
- close(fd);
- return on == '1';
+ if (feature == DriverFeature::FREEZE_NOTIFICATION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "freeze_notification");
+ return enabled;
+ }
+ return false;
}
status_t ProcessState::enableOnewaySpamDetection(bool enable) {
@@ -577,7 +601,7 @@
#ifdef __ANDROID__
LOG_ALWAYS_FATAL_IF(!opened.ok(),
"Binder driver '%s' could not be opened. Error: %s. Terminating.",
- error.c_str(), driver);
+ driver, error.c_str());
#endif
if (opened.ok()) {
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index b3a2d9e..65cdcd7 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,4 +32,34 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
+struct binder_frozen_state_info {
+ binder_uintptr_t cookie;
+ __u32 is_frozen;
+};
+
+#ifndef BR_FROZEN_BINDER
+// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it.
+#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info)
+#endif // BR_FROZEN_BINDER
+
+#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t)
+#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE
+
+#ifndef BC_REQUEST_FREEZE_NOTIFICATION
+// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie)
+#endif // BC_REQUEST_FREEZE_NOTIFICATION
+
+#ifndef BC_CLEAR_FREEZE_NOTIFICATION
+// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie)
+#endif // BC_CLEAR_FREEZE_NOTIFICATION
+
+#ifndef BC_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t)
+#endif // BC_FREEZE_NOTIFICATION_DONE
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index d7f74c4..7518044 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
// ---------------------------------------------------------------------------
namespace android {
+class IPCThreadState;
class RpcSession;
class RpcState;
namespace internal {
@@ -66,6 +67,12 @@
void* cookie = nullptr, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
void* cleanupCookie,
object_cleanup_func func) final;
@@ -75,7 +82,6 @@
LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID,
IBinder::object_make_func make,
const void* makeArgs);
-
LIBBINDER_EXPORTED virtual BpBinder* remoteBinder();
LIBBINDER_EXPORTED void sendObituary();
@@ -132,9 +138,14 @@
friend class ::android::ProcessState;
friend class ::android::RpcSession;
friend class ::android::RpcState;
- explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {}
+ friend class ::android::IPCThreadState;
+ explicit PrivateAccessor(const BpBinder* binder)
+ : mBinder(binder), mMutableBinder(nullptr) {}
+ explicit PrivateAccessor(BpBinder* binder) : mBinder(binder), mMutableBinder(binder) {}
- static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }
+ static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask) {
+ return BpBinder::create(handle, postTask);
+ }
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) {
return BpBinder::create(session, address);
}
@@ -146,17 +157,22 @@
uint64_t rpcAddress() const { return mBinder->rpcAddress(); }
const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
+ void onFrozenStateChanged(bool isFrozen) { mMutableBinder->onFrozenStateChanged(isFrozen); }
const BpBinder* mBinder;
+ BpBinder* mMutableBinder;
};
+
LIBBINDER_EXPORTED const PrivateAccessor getPrivateAccessor() const {
return PrivateAccessor(this);
}
+ PrivateAccessor getPrivateAccessor() { return PrivateAccessor(this); }
+
private:
friend PrivateAccessor;
friend class sp<BpBinder>;
- static sp<BpBinder> create(int32_t handle);
+ static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask);
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address);
struct BinderHandle {
@@ -192,6 +208,14 @@
uint32_t flags;
};
+ void onFrozenStateChanged(bool isFrozen);
+
+ struct FrozenStateChange {
+ bool isFrozen = false;
+ Vector<wp<FrozenStateChangeCallback>> callbacks;
+ bool initialStateReceived = false;
+ };
+
void reportOneDeath(const Obituary& obit);
bool isDescriptorCached() const;
@@ -199,6 +223,7 @@
volatile int32_t mAlive;
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
+ std::unique_ptr<FrozenStateChange> mFrozen;
ObjectManager mObjects;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 4eb1c08..1ed7c91 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -202,9 +202,18 @@
virtual void binderDied(const wp<IBinder>& who) = 0;
};
- #if defined(__clang__)
- #pragma clang diagnostic pop
- #endif
+ class FrozenStateChangeCallback : public virtual RefBase {
+ public:
+ enum class State {
+ FROZEN,
+ UNFROZEN,
+ };
+ virtual void onStateChanged(const wp<IBinder>& who, State state) = 0;
+ };
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
/**
* Register the @a recipient for a notification if this binder
@@ -253,6 +262,48 @@
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr) = 0;
+ /**
+ * addFrozenStateChangeCallback provides a callback mechanism to notify
+ * about process frozen/unfrozen events. Upon registration and any
+ * subsequent state changes, the callback is invoked with the latest process
+ * frozen state.
+ *
+ * If the listener process (the one using this API) is itself frozen, state
+ * change events might be combined into a single one with the latest state.
+ * (meaning 'frozen, unfrozen' might just be 'unfrozen'). This single event
+ * would then be delivered when the listener process becomes unfrozen.
+ * Similarly, if an event happens before the previous event is consumed,
+ * they might be combined. This means the callback might not be called for
+ * every single state change, so don't rely on this API to count how many
+ * times the state has changed.
+ *
+ * @note When all references to the binder are dropped, the callback is
+ * automatically removed. So, you must hold onto a binder in order to
+ * receive notifications about it.
+ *
+ * @note You will only receive freeze notifications for remote binders, as
+ * local binders by definition can't be frozen without you being frozen as
+ * well. Trying to use this function on a local binder will result in an
+ * INVALID_OPERATION code being returned and nothing happening.
+ *
+ * @note This binder always holds a weak reference to the callback.
+ *
+ * @note You will only receive a weak reference to the binder object. You
+ * should not try to promote this to a strong reference. (Nor should you
+ * need to, as there is nothing useful you can directly do with it now that
+ * it has passed on.)
+ */
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
+ /**
+ * Remove a previously registered freeze callback.
+ * The @a callback will no longer be called if this object
+ * changes its frozen state.
+ */
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
virtual bool checkSubclass(const void* subclassID) const;
typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 09ab442..9ef4e69 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -174,6 +174,8 @@
LIBBINDER_EXPORTED static void expungeHandle(int32_t handle, IBinder* binder);
LIBBINDER_EXPORTED status_t requestDeathNotification(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED status_t clearDeathNotification(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED static void shutdown();
@@ -210,13 +212,14 @@
IPCThreadState();
~IPCThreadState();
- status_t sendReply(const Parcel& reply, uint32_t flags);
- status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
- status_t talkWithDriver(bool doReceive = true);
- status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code,
- const Parcel& data, status_t* statusBuffer);
- status_t getAndExecuteCommand();
- status_t executeCommand(int32_t command);
+ [[nodiscard]] status_t sendReply(const Parcel& reply, uint32_t flags);
+ [[nodiscard]] status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
+ [[nodiscard]] status_t talkWithDriver(bool doReceive = true);
+ [[nodiscard]] status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle,
+ uint32_t code, const Parcel& data,
+ status_t* statusBuffer);
+ [[nodiscard]] status_t getAndExecuteCommand();
+ [[nodiscard]] status_t executeCommand(int32_t command);
void processPendingDerefs();
void processPostWriteDerefs();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 021bd58..21bfd42 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -133,6 +133,7 @@
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
+ FREEZE_NOTIFICATION,
};
// Determine whether a feature is supported by the binder driver.
LIBBINDER_EXPORTED static bool isDriverFeatureEnabled(const DriverFeature feature);
@@ -188,8 +189,8 @@
Vector<handle_entry> mHandleToObject;
bool mForked;
- bool mThreadPoolStarted;
- volatile int32_t mThreadPoolSeq;
+ std::atomic_bool mThreadPoolStarted;
+ std::atomic_int32_t mThreadPoolSeq;
CallRestriction mCallRestriction;
};
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 33dfe19..7f70396 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -1333,7 +1333,7 @@
let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
- let u16s = [u16::max_value(), 12_345, 42, 117];
+ let u16s = [u16::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1348,7 +1348,7 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::MAX
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
@@ -1361,9 +1361,9 @@
let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u16::MAX, 12_345, 42, 117]);
- let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+ let i16s = [i16::MAX, i16::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1378,8 +1378,8 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::MAX
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::MIN
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
@@ -1391,9 +1391,9 @@
let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+ assert_eq!(vec, [i16::MAX, i16::MIN, 42, -117]);
- let u32s = [u32::max_value(), 12_345, 42, 117];
+ let u32s = [u32::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1408,7 +1408,7 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::MAX
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
@@ -1421,9 +1421,9 @@
let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u32::MAX, 12_345, 42, 117]);
- let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+ let i32s = [i32::MAX, i32::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1438,8 +1438,8 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::MAX
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::MIN
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
@@ -1451,9 +1451,9 @@
let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+ assert_eq!(vec, [i32::MAX, i32::MIN, 42, -117]);
- let u64s = [u64::max_value(), 12_345, 42, 117];
+ let u64s = [u64::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1469,9 +1469,9 @@
let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u64::MAX, 12_345, 42, 117]);
- let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+ let i64s = [i64::MAX, i64::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1487,9 +1487,9 @@
let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+ assert_eq!(vec, [i64::MAX, i64::MIN, 42, -117]);
- let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON];
+ let f32s = [f32::NAN, f32::INFINITY, 1.23456789, f32::EPSILON];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1509,7 +1509,7 @@
assert!(vec[0].is_nan());
assert_eq!(vec[1..], f32s[1..]);
- let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON];
+ let f64s = [f64::NAN, f64::INFINITY, 1.234567890123456789, f64::EPSILON];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2b6c282..a902e96 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -124,7 +124,7 @@
bindings::Transaction_TEST_BYTE => {
assert_eq!(parcel.read::<i8>()?, 0);
assert_eq!(parcel.read::<i8>()?, 1);
- assert_eq!(parcel.read::<i8>()?, i8::max_value());
+ assert_eq!(parcel.read::<i8>()?, i8::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
// SAFETY: Just reading an extern constant.
@@ -133,7 +133,7 @@
reply.write(&0i8)?;
reply.write(&1i8)?;
- reply.write(&i8::max_value())?;
+ reply.write(&i8::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?;
// SAFETY: Just reading an extern constant.
@@ -143,14 +143,14 @@
bindings::Transaction_TEST_U16 => {
assert_eq!(parcel.read::<u16>()?, 0);
assert_eq!(parcel.read::<u16>()?, 1);
- assert_eq!(parcel.read::<u16>()?, u16::max_value());
+ assert_eq!(parcel.read::<u16>()?, u16::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS });
assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
reply.write(&0u16)?;
reply.write(&1u16)?;
- reply.write(&u16::max_value())?;
+ reply.write(&u16::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
reply.write(&(None as Option<Vec<u16>>))?;
@@ -158,14 +158,14 @@
bindings::Transaction_TEST_I32 => {
assert_eq!(parcel.read::<i32>()?, 0);
assert_eq!(parcel.read::<i32>()?, 1);
- assert_eq!(parcel.read::<i32>()?, i32::max_value());
+ assert_eq!(parcel.read::<i32>()?, i32::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 });
assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
reply.write(&0i32)?;
reply.write(&1i32)?;
- reply.write(&i32::max_value())?;
+ reply.write(&i32::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
reply.write(&(None as Option<Vec<i32>>))?;
@@ -173,14 +173,14 @@
bindings::Transaction_TEST_I64 => {
assert_eq!(parcel.read::<i64>()?, 0);
assert_eq!(parcel.read::<i64>()?, 1);
- assert_eq!(parcel.read::<i64>()?, i64::max_value());
+ assert_eq!(parcel.read::<i64>()?, i64::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 });
assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
reply.write(&0i64)?;
reply.write(&1i64)?;
- reply.write(&i64::max_value())?;
+ reply.write(&i64::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
reply.write(&(None as Option<Vec<i64>>))?;
@@ -188,14 +188,14 @@
bindings::Transaction_TEST_U64 => {
assert_eq!(parcel.read::<u64>()?, 0);
assert_eq!(parcel.read::<u64>()?, 1);
- assert_eq!(parcel.read::<u64>()?, u64::max_value());
+ assert_eq!(parcel.read::<u64>()?, u64::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 });
assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
reply.write(&0u64)?;
reply.write(&1u64)?;
- reply.write(&u64::max_value())?;
+ reply.write(&u64::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
reply.write(&(None as Option<Vec<u64>>))?;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index dd50fbd..21c32ac 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -42,6 +42,9 @@
defaults: ["binder_test_defaults"],
header_libs: ["libbinder_headers"],
srcs: ["binderDriverInterfaceTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ ],
test_suites: [
"device-tests",
"vts",
@@ -127,6 +130,7 @@
"libbase",
"libbinder",
"liblog",
+ "libprocessgroup",
"libutils",
],
static_libs: [
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 7be4f21..af82860 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -20,12 +20,15 @@
#include <stdlib.h>
#include <binder/IBinder.h>
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <linux/android/binder.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include "../binder_module.h"
+
#define BINDER_DEV_NAME "/dev/binder"
testing::Environment* binder_env;
@@ -365,6 +368,251 @@
binderTestReadEmpty();
}
+TEST_F(BinderDriverInterfaceTest, RequestFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ // Expecting a BR_FROZEN_BINDER since BC_REQUEST_FREEZE_NOTIFICATION
+ // above should lead to an immediate notification of the current state.
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ binder_uintptr_t arg2;
+ uint32_t cmd3;
+ struct binder_handle_cookie arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc2 = {
+ // Tell kernel that userspace has done handling BR_FROZEN_BINDER.
+ .cmd2 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg2 = cookie,
+ .cmd3 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg3 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr2);
+ EXPECT_EQ(sizeof(bc2), bwr2.write_consumed);
+ EXPECT_EQ(sizeof(br2) - sizeof(br2.pad), bwr2.read_consumed);
+ EXPECT_EQ(BR_NOOP, br2.cmd2);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br2.cmd3);
+ EXPECT_EQ(cookie, br2.arg3);
+
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, OverwritePendingFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ uint32_t cmd3;
+ uint32_t arg3;
+ } __attribute__((packed)) bc = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ // This BC_REQUEST_FREEZE_NOTIFICATION should lead to a pending
+ // frozen notification inserted into the queue.
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ // Send BC_CLEAR_FREEZE_NOTIFICATION before the above frozen
+ // notification has a chance of being sent. The notification should
+ // be overwritten. Userspace is expected to only receive
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd3 = BC_DECREFS,
+ .arg3 = 0,
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ binder_uintptr_t arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br;
+ struct binder_write_read bwr = binder_write_read();
+
+ bwr.write_buffer = (uintptr_t)&bc;
+ bwr.write_size = sizeof(bc);
+ bwr.read_buffer = (uintptr_t)&br;
+ bwr.read_size = sizeof(br);
+
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed);
+ EXPECT_EQ(BR_NOOP, br.cmd0);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br.cmd1);
+ EXPECT_EQ(cookie, br.arg1);
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, ResendFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ } __attribute__((packed)) bc2 = {
+ // Clear the notification before acknowledging the in-flight
+ // BR_FROZEN_BINDER. Kernel should hold off sending
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE until the acknowledgement
+ // reaches kernel.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+ struct {
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc3 = {
+ // Send the acknowledgement. Now the kernel should send out
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd3 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg3 = cookie,
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br3;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr2, EAGAIN);
+ binderTestReadEmpty();
+
+ struct binder_write_read bwr3 = binder_write_read();
+ bwr3.write_buffer = (uintptr_t)&bc3;
+ bwr3.write_size = sizeof(bc3);
+ bwr3.read_buffer = (uintptr_t)&br3;
+ bwr3.read_size = sizeof(br3);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr3);
+ EXPECT_EQ(sizeof(bc3), bwr3.write_consumed);
+ EXPECT_EQ(sizeof(br3) - sizeof(br3.pad), bwr3.read_consumed);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br3.cmd3);
+ EXPECT_EQ(cookie, br3.arg3);
+ binderTestReadEmpty();
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 9b1ba01..bcab6de 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -27,6 +27,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
#include <binder/Binder.h>
@@ -39,6 +40,8 @@
#include <binder/RpcSession.h>
#include <binder/Status.h>
#include <binder/unique_fd.h>
+#include <input/BlockingQueue.h>
+#include <processgroup/processgroup.h>
#include <utils/Flattenable.h>
#include <linux/sched.h>
@@ -57,6 +60,7 @@
using android::base::testing::HasValue;
using android::binder::Status;
using android::binder::unique_fd;
+using std::chrono_literals::operator""ms;
using testing::ExplainMatchResult;
using testing::Matcher;
using testing::Not;
@@ -115,6 +119,8 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_GETUID,
+ BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE,
+ BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
BINDER_LIB_TEST_REJECT_OBJECTS,
@@ -247,6 +253,43 @@
sp<IBinder> m_server;
};
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ BlockingQueue<std::pair<const wp<IBinder>, State>> events;
+
+ virtual void onStateChanged(const wp<IBinder>& who, State state) {
+ events.push(std::make_pair(who, state));
+ }
+
+ void ensureFrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::FROZEN, event->second); // isFrozen should be true
+ EXPECT_EQ(0u, events.size());
+ }
+
+ void ensureUnfrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::UNFROZEN, event->second); // isFrozen should be false
+ EXPECT_EQ(0u, events.size());
+ }
+
+ std::vector<bool> getAllAndClear() {
+ std::vector<bool> results;
+ while (true) {
+ auto event = events.popWithTimeout(0ms);
+ if (!event.has_value()) {
+ break;
+ }
+ results.push_back(event->second == State::FROZEN);
+ }
+ return results;
+ }
+
+ sp<IBinder> binder;
+};
+
class BinderLibTest : public ::testing::Test {
public:
virtual void SetUp() {
@@ -291,6 +334,51 @@
EXPECT_EQ(1, ret);
}
+ bool checkFreezeSupport() {
+ std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ // Pass test on devices where the cgroup v2 freezer is not supported
+ if (freezer_file.fail()) {
+ return false;
+ }
+ return IPCThreadState::self()->freeze(getpid(), false, 0) == NO_ERROR;
+ }
+
+ bool checkFreezeAndNotificationSupport() {
+ if (!checkFreezeSupport()) {
+ return false;
+ }
+ return ProcessState::isDriverFeatureEnabled(
+ ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ }
+
+ bool getBinderPid(int32_t* pid, sp<IBinder> server) {
+ Parcel data, replypid;
+ if (server->transact(BINDER_LIB_TEST_GETPID, data, &replypid) != NO_ERROR) {
+ ALOGE("BINDER_LIB_TEST_GETPID failed");
+ return false;
+ }
+ *pid = replypid.readInt32();
+ if (*pid <= 0) {
+ ALOGE("pid should be greater than zero");
+ return false;
+ }
+ return true;
+ }
+
+ void freezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
+ }
+
+ void unfreezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
+ }
+
+ void removeCallbackAndValidateNoEvent(sp<IBinder> binder,
+ sp<TestFrozenStateChangeCallback> callback) {
+ EXPECT_THAT(binder->removeFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ EXPECT_EQ(0u, callback->events.size());
+ }
+
sp<IBinder> m_server;
};
@@ -516,29 +604,18 @@
}
TEST_F(BinderLibTest, Freeze) {
- Parcel data, reply, replypid;
- std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
-
- // Pass test on devices where the cgroup v2 freezer is not supported
- if (freezer_file.fail()) {
- GTEST_SKIP();
+ if (!checkFreezeSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support proceess freezing";
return;
}
-
+ Parcel data, reply, replypid;
EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
for (int i = 0; i < 10; i++) {
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
}
- // Pass test on devices where BINDER_FREEZE ioctl is not supported
- int ret = IPCThreadState::self()->freeze(pid, false, 0);
- if (ret == -EINVAL) {
- GTEST_SKIP();
- return;
- }
- EXPECT_EQ(NO_ERROR, ret);
-
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
// b/268232063 - succeeds ~0.08% of the time
@@ -835,6 +912,199 @@
EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
}
+TEST_F(BinderLibTest, ReturnErrorIfKernelDoesNotSupportFreezeNotification) {
+ if (ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_EQ(nullptr, binder->localBinder());
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(INVALID_OPERATION));
+}
+
+TEST_F(BinderLibTest, FrozenStateChangeNotificatiion) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+}
+
+TEST_F(BinderLibTest, AddFrozenCallbackWhenFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ // Add the callback while the target process is frozen.
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ // Make sure no callback happens since the listener has been removed.
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, NoFrozenNotificationAfterCallbackRemoval) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Make sure no callback happens after the listener is removed.
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, MultipleFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback1 = sp<TestFrozenStateChangeCallback>::make();
+ sp<TestFrozenStateChangeCallback> callback2 = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback1), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback1->ensureUnfrozenEventReceived();
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback2), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback2->ensureUnfrozenEventReceived();
+
+ freezeProcess(pid);
+ callback1->ensureFrozenEventReceived();
+ callback2->ensureFrozenEventReceived();
+
+ removeCallbackAndValidateNoEvent(binder, callback1);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback1->events.size());
+ callback2->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback2);
+
+ freezeProcess(pid);
+ EXPECT_EQ(0u, callback2->events.size());
+}
+
+TEST_F(BinderLibTest, RemoveThenAddFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+}
+
+TEST_F(BinderLibTest, CoalesceFreezeCallbacksWhenListenerIsFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<IBinder> binder = addServer();
+ sp<IBinder> listener = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_NE(nullptr, listener);
+ int32_t pid, listenerPid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+ ASSERT_TRUE(getBinderPid(&listenerPid, listener));
+
+ // Ask the listener process to register for state change callbacks.
+ {
+ Parcel data, reply;
+ data.writeStrongBinder(binder);
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE, data,
+ &reply),
+ StatusEq(NO_ERROR));
+ }
+ // Freeze the listener process.
+ freezeProcess(listenerPid);
+ createProcessGroup(getuid(), listenerPid);
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Frozen"}));
+ // Repeatedly flip the target process between frozen and unfrozen states.
+ for (int i = 0; i < 1000; i++) {
+ usleep(50);
+ unfreezeProcess(pid);
+ usleep(50);
+ freezeProcess(pid);
+ }
+ // Unfreeze the listener process. Now it should receive the frozen state
+ // change notifications.
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Unfrozen"}));
+ unfreezeProcess(listenerPid);
+ // Wait for 500ms to give the process enough time to wake up and handle
+ // notifications.
+ usleep(500 * 1000);
+ {
+ std::vector<bool> events;
+ Parcel data, reply;
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS, data, &reply),
+ StatusEq(NO_ERROR));
+ reply.readBoolVector(&events);
+ // There should only be one single state change notifications delievered.
+ ASSERT_EQ(1u, events.size());
+ EXPECT_TRUE(events[0]);
+ }
+}
+
TEST_F(BinderLibTest, PassFile) {
int ret;
int pipefd[2];
@@ -1981,6 +2251,26 @@
reply->writeInt32(param.sched_priority);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE: {
+ sp<IBinder> binder = data.readStrongBinder();
+ frozenStateChangeCallback = sp<TestFrozenStateChangeCallback>::make();
+ // Hold an strong pointer to the binder object so it doesn't go
+ // away.
+ frozenStateChangeCallback->binder = binder;
+ int ret = binder->addFrozenStateChangeCallback(frozenStateChangeCallback);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ if (!event.has_value()) {
+ return NOT_ENOUGH_DATA;
+ }
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS: {
+ reply->writeBoolVector(frozenStateChangeCallback->getAllAndClear());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
@@ -2067,6 +2357,7 @@
sp<IBinder> m_callback;
bool m_exitOnDestroy;
std::mutex m_blockMutex;
+ sp<TestFrozenStateChangeCallback> frozenStateChangeCallback;
};
int run_server(int index, int readypipefd, bool usePoll)
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index b975fad..f867b42 100644
--- a/libs/binder/tests/binderRecordReplayTest.cpp
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -99,12 +99,12 @@
GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
Status setFileDescriptor(unique_fd input) {
- mFd = std::move(unique_fd(dup(input)));
+ mFd = unique_fd(dup(input));
return Status::ok();
}
Status getFileDescriptor(unique_fd* output) {
- *output = std::move(unique_fd(dup(mFd)));
+ *output = unique_fd(dup(mFd));
return Status::ok();
}
unique_fd mFd;
@@ -117,7 +117,7 @@
std::vector<uint8_t> buffer(fdStat.st_size);
auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size);
EXPECT_TRUE(readResult != 0);
- return std::move(buffer);
+ return buffer;
}
void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& transaction) {
@@ -387,8 +387,8 @@
// When fds are replayed, it will be replaced by /dev/null..reading from it should yield
// null data
- recordReplay(&IBinderRecordReplayTest::setFileDescriptor, std::move(unique_fd(dup(saved))),
- &IBinderRecordReplayTest::getFileDescriptor, std::move(unique_fd(dup(changed))));
+ recordReplay(&IBinderRecordReplayTest::setFileDescriptor, unique_fd(dup(saved)),
+ &IBinderRecordReplayTest::getFileDescriptor, unique_fd(dup(changed)));
}
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index f480780..2cec243 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -323,6 +323,22 @@
// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ virtual void onStateChanged(const wp<IBinder>&, State) {}
+};
+
+TEST_P(BinderRpc, RpcBinderShouldFailOnFrozenStateCallbacks) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ EXPECT_DEATH_IF_SUPPORTED(
+ { std::ignore = a->addFrozenStateChangeCallback(callback); },
+ "addFrozenStateChangeCallback\\(\\) is not supported for RPC Binder.");
+}
+
TEST_P(BinderRpc, RepeatRootObject) {
auto proc = createRpcTestSocketServerProcess({});
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
index fd9777a..0ed8a55 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
@@ -55,7 +55,7 @@
offset += CHAR_BIT;
}
- return std::move(reverseData);
+ return reverseData;
}
template <typename T>
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 23f583b..7f45581 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -125,6 +125,11 @@
VULKAN_DEVICE_EXTENSION = 9,
};
+ enum GLTelemetryHints {
+ NO_HINT = 0,
+ SKIP_TELEMETRY = 1,
+ };
+
GpuStatsInfo() = default;
GpuStatsInfo(const GpuStatsInfo&) = default;
virtual ~GpuStatsInfo() = default;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 51d2e53..1243b21 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -255,6 +255,7 @@
"BitTube.cpp",
"BLASTBufferQueue.cpp",
"BufferItemConsumer.cpp",
+ "BufferReleaseChannel.cpp",
"Choreographer.cpp",
"CompositorTiming.cpp",
"ConsumerBase.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 739c3c2..cda4985 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -20,6 +20,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+#include <com_android_graphics_libgui_flags.h>
#include <cutils/atomic.h>
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
@@ -39,7 +40,6 @@
#include <private/gui/ComposerServiceAIDL.h>
#include <android-base/thread_annotations.h>
-#include <chrono>
#include <com_android_graphics_libgui_flags.h>
@@ -175,16 +175,21 @@
mSyncTransaction(nullptr),
mUpdateDestinationFrame(updateDestinationFrame) {
createBufferQueue(&mProducer, &mConsumer);
- // since the adapter is in the client process, set dequeue timeout
- // explicitly so that dequeueBuffer will block
- mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
-
- // safe default, most producers are expected to override this
- mProducer->setMaxDequeuedBufferCount(2);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
+#else
mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_HW_TEXTURE,
1, false, this);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // since the adapter is in the client process, set dequeue timeout
+ // explicitly so that dequeueBuffer will block
+ mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
+
static std::atomic<uint32_t> nextId = 0;
mProducerId = nextId++;
mName = name + "#" + std::to_string(mProducerId);
@@ -236,6 +241,11 @@
}
}
+void BLASTBufferQueue::onFirstRef() {
+ // safe default, most producers are expected to override this
+ mProducer->setMaxDequeuedBufferCount(2);
+}
+
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
int32_t format) {
LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
@@ -499,7 +509,13 @@
callbackId.to_string().c_str());
return;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ if (!it->second.mIsStale) {
+ mNumAcquired--;
+ }
+#else
mNumAcquired--;
+#endif
BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber);
BQA_LOGV("released %s", callbackId.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
@@ -761,6 +777,9 @@
}
// add to shadow queue
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mNumDequeued--;
+#endif
mNumFrameAvailable++;
if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
acquireAndReleaseBuffer();
@@ -815,9 +834,18 @@
};
void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
- std::lock_guard _lock{mTimestampMutex};
- mDequeueTimestamps.erase(bufferId);
-};
+ {
+ std::lock_guard _lock{mTimestampMutex};
+ mDequeueTimestamps.erase(bufferId);
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ {
+ std::lock_guard lock{mMutex};
+ mNumDequeued--;
+ }
+#endif
+}
bool BLASTBufferQueue::syncNextTransaction(
std::function<void(SurfaceComposerClient::Transaction*)> callback,
@@ -1116,30 +1144,143 @@
producerControlledByApp, output);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ status_t disconnect(int api, DisconnectMode mode) override {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return BufferQueueProducer::disconnect(api, mode);
+ }
+
+ std::lock_guard lock{bbq->mMutex};
+ if (status_t status = BufferQueueProducer::disconnect(api, mode); status != OK) {
+ return status;
+ }
+
+ bbq->mNumDequeued = 0;
+ bbq->mNumAcquired = 0;
+ for (auto& [releaseId, bufferItem] : bbq->mSubmitted) {
+ bufferItem.mIsStale = true;
+ }
+
+ return OK;
+ }
+
+ status_t setAsyncMode(bool asyncMode) override {
+ if (status_t status = BufferQueueProducer::setAsyncMode(asyncMode); status != OK) {
+ return status;
+ }
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mAsyncMode = asyncMode;
+ }
+
+ return OK;
+ }
+
+ status_t setSharedBufferMode(bool sharedBufferMode) override {
+ if (status_t status = BufferQueueProducer::setSharedBufferMode(sharedBufferMode);
+ status != OK) {
+ return status;
+ }
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mSharedBufferMode = sharedBufferMode;
+ }
+
+ return OK;
+ }
+
+ status_t detachBuffer(int slot) override {
+ if (status_t status = BufferQueueProducer::detachBuffer(slot); status != OK) {
+ return status;
+ }
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mNumDequeued--;
+ }
+
+ return OK;
+ }
+
+ status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, uint32_t height,
+ PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+ FrameEventHistoryDelta* outTimestamps) override {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format,
+ usage, outBufferAge, outTimestamps);
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mNumDequeued++;
+ }
+
+ status_t status =
+ BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, usage,
+ outBufferAge, outTimestamps);
+ if (status < 0) {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mNumDequeued--;
+ }
+ return status;
+ }
+#endif
+
// We want to resize the frame history when changing the size of the buffer queue
status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override {
int maxBufferCount;
- status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
- &maxBufferCount);
- // if we can't determine the max buffer count, then just skip growing the history size
- if (status == OK) {
- size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
- // optimize away resizing the frame history unless it will grow
- if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
- sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
- if (bbq != nullptr) {
- ALOGV("increasing frame history size to %zu", newFrameHistorySize);
- bbq->resizeFrameEventHistory(newFrameHistorySize);
- }
- }
+ if (status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
+ &maxBufferCount);
+ status != OK) {
+ return status;
}
- return status;
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ // if we can't determine the max buffer count, then just skip growing the history size
+ size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
+ // optimize away resizing the frame history unless it will grow
+ if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
+ ALOGV("increasing frame history size to %zu", newFrameHistorySize);
+ bbq->resizeFrameEventHistory(newFrameHistorySize);
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mMaxDequeuedBuffers = maxDequeuedBufferCount;
+ }
+#endif
+
+ return OK;
}
int query(int what, int* value) override {
if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
*value = 1;
- return NO_ERROR;
+ return OK;
}
return BufferQueueProducer::query(what, value);
}
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index eeb8f19..bfe3d6e 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -35,18 +35,37 @@
namespace android {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp, bool isConsumerSurfaceFlinger)
+ : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger) {
+ initialize(consumerUsage, bufferCount);
+}
+
+BufferItemConsumer::BufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp)
+ : ConsumerBase(producer, consumer, controlledByApp) {
+ initialize(consumerUsage, bufferCount);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
BufferItemConsumer::BufferItemConsumer(
const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount, bool controlledByApp) :
ConsumerBase(consumer, controlledByApp)
{
+ initialize(consumerUsage, bufferCount);
+}
+
+void BufferItemConsumer::initialize(uint64_t consumerUsage, int bufferCount) {
status_t err = mConsumer->setConsumerUsageBits(consumerUsage);
- LOG_ALWAYS_FATAL_IF(err != OK,
- "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
if (bufferCount != DEFAULT_MAX_BUFFERS) {
err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
- LOG_ALWAYS_FATAL_IF(err != OK,
- "Failed to set max acquired buffer count to %d", bufferCount);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set max acquired buffer count to %d",
+ bufferCount);
}
}
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
new file mode 100644
index 0000000..27367aa
--- /dev/null
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferReleaseChannel"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <android-base/result.h>
+#include <android/binder_status.h>
+#include <binder/Parcel.h>
+#include <utils/Flattenable.h>
+
+#include <gui/BufferReleaseChannel.h>
+#include <private/gui/ParcelUtils.h>
+
+using android::base::Result;
+
+namespace android::gui {
+
+namespace {
+
+template <typename T>
+static void readAligned(const void*& buffer, size_t& size, T& value) {
+ size -= FlattenableUtils::align<alignof(T)>(buffer);
+ FlattenableUtils::read(buffer, size, value);
+}
+
+template <typename T>
+static void writeAligned(void*& buffer, size_t& size, T value) {
+ size -= FlattenableUtils::align<alignof(T)>(buffer);
+ FlattenableUtils::write(buffer, size, value);
+}
+
+template <typename T>
+static void addAligned(size_t& size, T /* value */) {
+ size = FlattenableUtils::align<sizeof(T)>(size);
+ size += sizeof(T);
+}
+
+template <typename T>
+static inline constexpr uint32_t low32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n));
+}
+
+template <typename T>
+static inline constexpr uint32_t high32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
+}
+
+template <typename T>
+static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+ return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
+}
+
+} // namespace
+
+size_t BufferReleaseChannel::Message::getPodSize() const {
+ size_t size = 0;
+ addAligned(size, low32(releaseCallbackId.bufferId));
+ addAligned(size, high32(releaseCallbackId.bufferId));
+ addAligned(size, low32(releaseCallbackId.framenumber));
+ addAligned(size, high32(releaseCallbackId.framenumber));
+ addAligned(size, maxAcquiredBufferCount);
+ return size;
+}
+
+size_t BufferReleaseChannel::Message::getFlattenedSize() const {
+ size_t size = releaseFence->getFlattenedSize();
+ size = FlattenableUtils::align<4>(size);
+ size += getPodSize();
+ return size;
+}
+
+status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) {
+ return err;
+ }
+ size -= FlattenableUtils::align<4>(buffer);
+
+ // Check we still have enough space
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ writeAligned(buffer, size, low32(releaseCallbackId.bufferId));
+ writeAligned(buffer, size, high32(releaseCallbackId.bufferId));
+ writeAligned(buffer, size, low32(releaseCallbackId.framenumber));
+ writeAligned(buffer, size, high32(releaseCallbackId.framenumber));
+ writeAligned(buffer, size, maxAcquiredBufferCount);
+ return OK;
+}
+
+status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ releaseFence = new Fence();
+ if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
+ return err;
+ }
+ size -= FlattenableUtils::align<4>(buffer);
+
+ // Check we still have enough space
+ if (size < getPodSize()) {
+ return OK;
+ }
+
+ uint32_t bufferIdLo = 0, bufferIdHi = 0;
+ uint32_t frameNumberLo = 0, frameNumberHi = 0;
+
+ readAligned(buffer, size, bufferIdLo);
+ readAligned(buffer, size, bufferIdHi);
+ releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi);
+ readAligned(buffer, size, frameNumberLo);
+ readAligned(buffer, size, frameNumberHi);
+ releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
+ readAligned(buffer, size, maxAcquiredBufferCount);
+
+ return OK;
+}
+
+status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
+ ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
+ uint32_t& outMaxAcquiredBufferCount) {
+ Message message;
+ mFlattenedBuffer.resize(message.getFlattenedSize());
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+
+ iovec iov{
+ .iov_base = mFlattenedBuffer.data(),
+ .iov_len = mFlattenedBuffer.size(),
+ };
+
+ msghdr msg{
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = controlMessageBuffer.data(),
+ .msg_controllen = controlMessageBuffer.size(),
+ };
+
+ int result;
+ do {
+ result = recvmsg(mFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ return WOULD_BLOCK;
+ }
+ ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ if (msg.msg_iovlen != 1) {
+ ALOGE("Error reading release fence from socket: bad data length");
+ return UNKNOWN_ERROR;
+ }
+
+ if (msg.msg_controllen % sizeof(int) != 0) {
+ ALOGE("Error reading release fence from socket: bad fd length");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t dataLen = msg.msg_iov->iov_len;
+ const void* data = static_cast<const void*>(msg.msg_iov->iov_base);
+ if (!data) {
+ ALOGE("Error reading release fence from socket: no buffer data");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t fdCount = 0;
+ const int* fdData = nullptr;
+ if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) {
+ fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+ fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ }
+
+ if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) {
+ return err;
+ }
+
+ outReleaseCallbackId = message.releaseCallbackId;
+ outReleaseFence = std::move(message.releaseFence);
+ outMaxAcquiredBufferCount = message.maxAcquiredBufferCount;
+
+ return OK;
+}
+
+int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
+ const sp<Fence>& fence,
+ uint32_t maxAcquiredBufferCount) {
+ Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
+ mFlattenedBuffer.resize(message.getFlattenedSize());
+ int flattenedFd;
+ {
+ // Make copies of needed items since flatten modifies them, and we don't
+ // want to send anything if there's an error during flatten.
+ void* flattenedBufferPtr = mFlattenedBuffer.data();
+ size_t flattenedBufferSize = mFlattenedBuffer.size();
+ int* flattenedFdPtr = &flattenedFd;
+ size_t flattenedFdCount = 1;
+ if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
+ flattenedFdCount);
+ err != OK) {
+ ALOGE("Failed to flatten BufferReleaseChannel message.");
+ return err;
+ }
+ }
+
+ iovec iov{
+ .iov_base = mFlattenedBuffer.data(),
+ .iov_len = mFlattenedBuffer.size(),
+ };
+
+ msghdr msg{
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ if (fence && fence->isValid()) {
+ msg.msg_control = controlMessageBuffer.data();
+ msg.msg_controllen = controlMessageBuffer.size();
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
+ }
+
+ int result;
+ do {
+ result = sendmsg(mFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) {
+ if (!parcel) return STATUS_BAD_VALUE;
+ SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName);
+ SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd);
+ return STATUS_OK;
+}
+
+status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const {
+ if (!parcel) return STATUS_BAD_VALUE;
+ SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName);
+ SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd);
+ return STATUS_OK;
+}
+
+status_t BufferReleaseChannel::open(std::string name,
+ std::unique_ptr<ConsumerEndpoint>& outConsumer,
+ std::shared_ptr<ProducerEndpoint>& outProducer) {
+ outConsumer.reset();
+ outProducer.reset();
+
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
+ ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ android::base::unique_fd consumerFd(sockets[0]);
+ android::base::unique_fd producerFd(sockets[1]);
+
+ // Socket buffer size. The default is typically about 128KB, which is much larger than
+ // we really need.
+ size_t bufferSize = 32 * 1024;
+ if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Configure the consumer socket to be non-blocking.
+ int flags = fcntl(consumerFd.get(), F_GETFL, 0);
+ if (flags == -1) {
+ ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(),
+ errno, strerror(errno));
+ return -errno;
+ }
+ if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
+ ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Configure a timeout for the producer socket.
+ const timeval timeout{.tv_sec = 1, .tv_usec = 0};
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) {
+ ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(),
+ errno, strerror(errno));
+ return -errno;
+ }
+
+ // Make the consumer read-only
+ if (shutdown(consumerFd.get(), SHUT_WR) == -1) {
+ ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Make the producer write-only
+ if (shutdown(producerFd.get(), SHUT_RD) == -1) {
+ ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
+ outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
+ return STATUS_OK;
+}
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 19b9c8b..602bba8 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <inttypes.h>
-
#define LOG_TAG "ConsumerBase"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -29,19 +27,22 @@
#include <cutils/atomic.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <ui/BufferQueueDefs.h>
#include <private/gui/ComposerService.h>
+#include <log/log.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-#include <com_android_graphics_libgui_flags.h>
+#include <inttypes.h>
// Macros for including the ConsumerBase name in log messages
#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -62,6 +63,30 @@
mAbandoned(false),
mConsumer(bufferQueue),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ initialize(controlledByApp);
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger)
+ : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
+ mSurface = sp<Surface>::make(producer, controlledByApp);
+ initialize(controlledByApp);
+}
+
+ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp)
+ : mAbandoned(false),
+ mConsumer(consumer),
+ mSurface(sp<Surface>::make(producer, controlledByApp)),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ initialize(controlledByApp);
+}
+
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+void ConsumerBase::initialize(bool controlledByApp) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -355,6 +380,17 @@
return mConsumer->setTransformHint(hint);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+status_t ConsumerBase::setMaxBufferCount(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->setMaxBufferCount(bufferCount);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -364,6 +400,17 @@
return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("setConsumerIsProtected: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->setConsumerIsProtected(isProtected);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
sp<NativeHandle> ConsumerBase::getSidebandStream() const {
Mutex::Autolock _l(mMutex);
if (mAbandoned) {
@@ -430,6 +477,19 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+sp<Surface> ConsumerBase::getSurface() const {
+ LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
+ "It's illegal to get the surface of a Consumer that does not own it. This "
+ "should be impossible once the old CTOR is removed.");
+ return mSurface;
+}
+
+sp<IGraphicBufferConsumer> ConsumerBase::getIGraphicBufferConsumer() const {
+ return mConsumer;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
nsecs_t presentWhen, uint64_t maxFrameNumber) {
if (mAbandoned) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 3031fa1..23b432e 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -18,9 +18,9 @@
#define LOG_TAG "CpuConsumer"
//#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <gui/CpuConsumer.h>
-
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
+#include <gui/CpuConsumer.h>
#include <utils/Log.h>
#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -31,12 +31,25 @@
namespace android {
-CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
- size_t maxLockedBuffers, bool controlledByApp) :
- ConsumerBase(bq, controlledByApp),
- mMaxLockedBuffers(maxLockedBuffers),
- mCurrentLockedBuffers(0)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp,
+ bool isConsumerSurfaceFlinger)
+ : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger),
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0) {
+ // Create tracking entries for locked buffers
+ mAcquiredBuffers.insertAt(0, maxLockedBuffers);
+
+ mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN);
+ mConsumer->setMaxAcquiredBufferCount(static_cast<int32_t>(maxLockedBuffers));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp)
+ : ConsumerBase(bq, controlledByApp),
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0) {
// Create tracking entries for locked buffers
mAcquiredBuffers.insertAt(0, maxLockedBuffers);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index d49489c..95cce5c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -101,6 +101,34 @@
return hasIt;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(true) {
+ GLC_LOGV("GLConsumer");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp),
@@ -130,27 +158,54 @@
mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
-GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
- bool useFenceSync, bool isControlledByApp) :
- ConsumerBase(bq, isControlledByApp),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
- mCurrentFrameNumber(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mTexName(0),
- mUseFenceSync(useFenceSync),
- mTexTarget(texTarget),
- mEglDisplay(EGL_NO_DISPLAY),
- mEglContext(EGL_NO_CONTEXT),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
- mAttached(false)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+GLConsumer::GLConsumer(uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(false) {
+ GLC_LOGV("GLConsumer");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, bool useFenceSync,
+ bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(false) {
GLC_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 307ae39..0714fd9 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "LayerState"
#include <cinttypes>
-#include <cmath>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
@@ -194,6 +193,13 @@
SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio);
SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio);
SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint));
+
+ const bool hasBufferReleaseChannel = (bufferReleaseChannel != nullptr);
+ SAFE_PARCEL(output.writeBool, hasBufferReleaseChannel);
+ if (hasBufferReleaseChannel) {
+ SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
+ }
+
return NO_ERROR;
}
@@ -339,6 +345,13 @@
SAFE_PARCEL(input.readInt32, &tmpInt32);
cachingHint = static_cast<gui::CachingHint>(tmpInt32);
+ bool hasBufferReleaseChannel;
+ SAFE_PARCEL(input.readBool, &hasBufferReleaseChannel);
+ if (hasBufferReleaseChannel) {
+ bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
+ SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
+ }
+
return NO_ERROR;
}
@@ -718,6 +731,10 @@
if (other.what & eFlushJankData) {
what |= eFlushJankData;
}
+ if (other.what & eBufferReleaseChannelChanged) {
+ what |= eBufferReleaseChannelChanged;
+ bufferReleaseChannel = other.bufferReleaseChannel;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -797,6 +814,7 @@
CHECK_DIFF(diff, eColorChanged, other, color.rgb);
CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
+ if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
return diff;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index a98a3c0..4a8df9e 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2027,15 +2027,6 @@
return connect(api, listener);
}
-int Surface::connect(int api, const sp<SurfaceListener>& listener) {
- return connect(api, listener, false);
-}
-
-int Surface::connect(
- int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) {
- return connect(api, sListener, reportBufferRemoval);
-}
-
int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
ALOGV("Surface::connect");
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e86e13d..b5d9366 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -827,7 +827,6 @@
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
: mId(other.mId),
- mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeupStart(other.mEarlyWakeupStart),
mEarlyWakeupEnd(other.mEarlyWakeupEnd),
@@ -867,7 +866,6 @@
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint64_t transactionId = parcel->readUint64();
- const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
const bool earlyWakeupStart = parcel->readBool();
const bool earlyWakeupEnd = parcel->readBool();
@@ -964,7 +962,6 @@
// Parsing was successful. Update the object.
mId = transactionId;
- mTransactionNestCount = transactionNestCount;
mAnimation = animation;
mEarlyWakeupStart = earlyWakeupStart;
mEarlyWakeupEnd = earlyWakeupEnd;
@@ -996,7 +993,6 @@
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
parcel->writeUint64(mId);
- parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
parcel->writeBool(mEarlyWakeupStart);
parcel->writeBool(mEarlyWakeupEnd);
@@ -1148,7 +1144,6 @@
mInputWindowCommands.clear();
mUncacheBuffers.clear();
mMayContainBuffer = false;
- mTransactionNestCount = 0;
mAnimation = false;
mEarlyWakeupStart = false;
mEarlyWakeupEnd = false;
@@ -2395,6 +2390,22 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferReleaseChannel(
+ const sp<SurfaceControl>& sc,
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBufferReleaseChannelChanged;
+ s->bufferReleaseChannel = channel;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 0e1a505..4dafc57 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-
+#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/SurfaceComposerClient.h>
@@ -28,7 +29,6 @@
#include <utils/RefBase.h>
#include <system/window.h>
-#include <thread>
#include <queue>
#include <com_android_graphics_libgui_flags.h>
@@ -40,12 +40,20 @@
class BLASTBufferItemConsumer : public BufferItemConsumer {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+ : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
+#else
BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
: BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mBLASTBufferQueue(std::move(bbq)),
mCurrentlyConnected(false),
- mPreviouslyConnected(false) {}
+ mPreviouslyConnected(false) {
+ }
void onDisconnect() override EXCLUDES(mMutex);
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -131,6 +139,8 @@
virtual ~BLASTBufferQueue();
+ void onFirstRef() override;
+
private:
friend class BLASTBufferQueueHelper;
friend class BBQBufferQueueProducer;
@@ -170,8 +180,16 @@
// BufferQueue internally allows 1 more than
// the max to be acquired
- int32_t mMaxAcquiredBuffers = 1;
+ int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ int32_t mMaxDequeuedBuffers GUARDED_BY(mMutex) = 1;
+ static constexpr int32_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
+ bool mAsyncMode GUARDED_BY(mMutex) = false;
+ bool mSharedBufferMode GUARDED_BY(mMutex) = false;
+
+ int32_t mNumDequeued GUARDED_BY(mMutex) = 0;
+#endif
int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0;
int32_t mNumAcquired GUARDED_BY(mMutex) = 0;
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index e383a40..6810eda 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -53,9 +53,17 @@
// access at the same time.
// controlledByApp tells whether this consumer is controlled by the
// application.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferItemConsumer(uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+ BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer,
uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
~BufferItemConsumer() override;
@@ -92,7 +100,17 @@
const sp<Fence>& releaseFence = Fence::NO_FENCE);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+protected:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This should only be used by BLASTBufferQueue:
+ BufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
private:
+ void initialize(uint64_t consumerUsage, int bufferCount);
+
status_t releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
const sp<Fence>& releaseFence);
diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h
new file mode 100644
index 0000000..51fe0b6
--- /dev/null
+++ b/libs/gui/include/gui/BufferReleaseChannel.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include <binder/Parcelable.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+#include <utils/Errors.h>
+
+namespace android::gui {
+
+/**
+ * IPC wrapper to pass release fences from SurfaceFlinger to apps via a local unix domain socket.
+ */
+class BufferReleaseChannel {
+private:
+ class Endpoint {
+ public:
+ Endpoint(std::string name, android::base::unique_fd fd)
+ : mName(std::move(name)), mFd(std::move(fd)) {}
+ Endpoint() {}
+
+ Endpoint(Endpoint&&) noexcept = default;
+ Endpoint& operator=(Endpoint&&) noexcept = default;
+
+ Endpoint(const Endpoint&) = delete;
+ void operator=(const Endpoint&) = delete;
+
+ const android::base::unique_fd& getFd() const { return mFd; }
+
+ protected:
+ std::string mName;
+ android::base::unique_fd mFd;
+ };
+
+public:
+ class ConsumerEndpoint : public Endpoint {
+ public:
+ ConsumerEndpoint(std::string name, android::base::unique_fd fd)
+ : Endpoint(std::move(name), std::move(fd)) {}
+
+ /**
+ * Reads a release fence from the BufferReleaseChannel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no fence present.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t readReleaseFence(ReleaseCallbackId& outReleaseCallbackId,
+ sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount);
+
+ private:
+ std::vector<uint8_t> mFlattenedBuffer;
+ };
+
+ class ProducerEndpoint : public Endpoint, public Parcelable {
+ public:
+ ProducerEndpoint(std::string name, android::base::unique_fd fd)
+ : Endpoint(std::move(name), std::move(fd)) {}
+ ProducerEndpoint() {}
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+ status_t writeReleaseFence(const ReleaseCallbackId&, const sp<Fence>& releaseFence,
+ uint32_t maxAcquiredBufferCount);
+
+ private:
+ std::vector<uint8_t> mFlattenedBuffer;
+ };
+
+ /**
+ * Create two endpoints that make up the BufferReleaseChannel.
+ *
+ * Return OK on success.
+ */
+ static status_t open(const std::string name, std::unique_ptr<ConsumerEndpoint>& outConsumer,
+ std::shared_ptr<ProducerEndpoint>& outProducer);
+
+ struct Message : public Flattenable<Message> {
+ ReleaseCallbackId releaseCallbackId;
+ sp<Fence> releaseFence = Fence::NO_FENCE;
+ uint32_t maxAcquiredBufferCount;
+
+ Message() = default;
+ Message(ReleaseCallbackId releaseCallbackId, sp<Fence> releaseFence,
+ uint32_t maxAcquiredBufferCount)
+ : releaseCallbackId{releaseCallbackId},
+ releaseFence{std::move(releaseFence)},
+ maxAcquiredBufferCount{maxAcquiredBufferCount} {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const;
+
+ size_t getFdCount() const { return releaseFence->getFdCount(); }
+
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ private:
+ size_t getPodSize() const;
+ };
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index a031e66..e976aa4 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -21,6 +21,7 @@
#include <gui/BufferQueueDefs.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/OccupancyTracker.h>
#include <ui/PixelFormat.h>
#include <utils/String8.h>
@@ -74,6 +75,16 @@
void dumpState(String8& result) const;
void dumpState(String8& result, const char* prefix) const;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // Returns a Surface that can be used as the producer for this consumer.
+ sp<Surface> getSurface() const;
+
+ // DEPRECATED, DO NOT USE. Returns the underlying IGraphicBufferConsumer
+ // that backs this ConsumerBase.
+ sp<IGraphicBufferConsumer> getIGraphicBufferConsumer() const
+ __attribute((deprecated("DO NOT USE: Temporary hack for refactoring")));
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
@@ -102,9 +113,18 @@
// See IGraphicBufferConsumer::setTransformHint
status_t setTransformHint(uint32_t hint);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // See IGraphicBufferConsumer::setMaxBufferCount
+ status_t setMaxBufferCount(int bufferCount);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// See IGraphicBufferConsumer::setMaxAcquiredBufferCount
status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ status_t setConsumerIsProtected(bool isProtected);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// See IGraphicBufferConsumer::getSidebandStream
sp<NativeHandle> getSidebandStream() const;
@@ -119,12 +139,24 @@
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
+ void initialize(bool controlledByApp);
+
protected:
// ConsumerBase constructs a new ConsumerBase object to consume image
// buffers from the given IGraphicBufferConsumer.
// The controlledByApp flag indicates that this consumer is under the application's
// control.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ explicit ConsumerBase(bool controlledByApp = false, bool consumerIsSurfaceFlinger = false);
+ explicit ConsumerBase(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+
+ explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false)
+ __attribute((deprecated("ConsumerBase should own its own producer, and constructing it "
+ "without one is fragile! This method is going away soon.")));
+#else
explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// onLastStrongRef gets called by RefBase just before the dtor of the most
// derived class. It is used to clean up the buffers so that ConsumerBase
@@ -272,10 +304,16 @@
Mutex mFrameAvailableMutex;
wp<FrameAvailableListener> mFrameAvailableListener;
- // The ConsumerBase has-a BufferQueue and is responsible for creating this object
- // if none is supplied
+ // The ConsumerBase has-a BufferQueue and is responsible for creating these
+ // objects if not supplied.
sp<IGraphicBufferConsumer> mConsumer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This Surface wraps the IGraphicBufferConsumer created for this
+ // ConsumerBase.
+ sp<Surface> mSurface;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// The final release fence of the most recent buffer released by
// releaseBufferLocked.
sp<Fence> mPrevFinalReleaseFence;
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 806fbe8..2bba61b 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -19,8 +19,9 @@
#include <system/window.h>
-#include <gui/ConsumerBase.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueue.h>
+#include <gui/ConsumerBase.h>
#include <utils/Vector.h>
@@ -91,8 +92,17 @@
// Create a new CPU consumer. The maxLockedBuffers parameter specifies
// how many buffers can be locked for user access at the same time.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false,
+ bool isConsumerSurfaceFlinger = false);
+
+ CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
size_t maxLockedBuffers, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// Gets the next graphics buffer from the producer and locks it for CPU use,
// filling out the passed-in locked buffer structure with the native pointer
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index ba268ab..bfe3eb3 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
@@ -82,12 +83,25 @@
// If the constructor without the tex parameter is used, the GLConsumer is
// created in a detached state, and attachToContext must be called before
// calls to updateTexImage.
- GLConsumer(const sp<IGraphicBufferConsumer>& bq,
- uint32_t tex, uint32_t texureTarget, bool useFenceSync,
- bool isControlledByApp);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
- GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget,
- bool useFenceSync, bool isControlledByApp);
+ GLConsumer(uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// updateTexImage acquires the most recently queued buffer, and sets the
// image contents of the target texture to it.
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 3fb1894..d419945 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -34,6 +34,7 @@
#include <android/gui/TrustedOverlay.h>
#include <ftl/flags.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/DisplayCaptureArgs.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerCaptureArgs.h>
@@ -220,6 +221,7 @@
eDropInputModeChanged = 0x8000'00000000,
eExtendedRangeBrightnessChanged = 0x10000'00000000,
eEdgeExtensionChanged = 0x20000'00000000,
+ eBufferReleaseChannelChanged = 0x40000'00000000,
};
layer_state_t();
@@ -412,6 +414,8 @@
TrustedPresentationThresholds trustedPresentationThresholds;
TrustedPresentationListener trustedPresentationListener;
+
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
};
class ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 3e2aa47..39207f8 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -381,20 +381,15 @@
virtual int unlockAndPost();
virtual int query(int what, int* value) const;
- virtual int connect(int api, const sp<SurfaceListener>& listener);
-
// When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch
// GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or
// attachBuffer call. This allows clients with their own buffer caches to free up buffers no
// longer in use by this surface.
- virtual int connect(int api, const sp<SurfaceListener>& listener, bool reportBufferRemoval);
- virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
+ virtual int connect(int api, const sp<SurfaceListener>& listener,
+ bool reportBufferRemoval = false);
+ virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
virtual int attachBuffer(ANativeWindowBuffer*);
- virtual int connect(
- int api, bool reportBufferRemoval,
- const sp<SurfaceListener>& sListener);
virtual void destroy();
// When client connects to Surface with reportBufferRemoval set to true, any buffers removed
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 95574ee..4f9af16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -45,6 +45,7 @@
#include <android/gui/BnJankListener.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
@@ -451,7 +452,6 @@
uint64_t mId;
- uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
bool mEarlyWakeupStart = false;
bool mEarlyWakeupEnd = false;
@@ -763,6 +763,10 @@
const Rect& destinationFrame);
Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
+ Transaction& setBufferReleaseChannel(
+ const sp<SurfaceControl>& sc,
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 9d44cc9..c367e75 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -10,6 +10,14 @@
} # bq_setframerate
flag {
+ name: "bq_consumer_attach_callback"
+ namespace: "core_graphics"
+ description: "Controls IProducerListener to have consumer side attach callback"
+ bug: "353202582"
+ is_fixed_read_only: true
+} # bq_consumer_attach_callback
+
+flag {
name: "frametimestamps_previousrelease"
namespace: "core_graphics"
description: "Controls a fence fixup for timestamp apis"
@@ -77,17 +85,9 @@
} # buffer_release_channel
flag {
- name: "wb_surface_connect_methods"
+ name: "wb_ring_buffer"
namespace: "core_graphics"
- description: "Remove redundant connect methods in Surface."
- bug: "354273690"
+ description: "Remove slot dependency in the Ring Buffer Consumer."
+ bug: "342197847"
is_fixed_read_only: true
-} # wb_surface_connect_methods
-
-flag {
- name: "bq_consumer_attach_callback"
- namespace: "core_graphics"
- description: "Controls IProducerListener to have consumer side attach callback"
- bug: "353202582"
- is_fixed_read_only: true
-} # bq_consumer_attach_callback
+} # wb_ring_buffer
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index b342a7d..1b216e9 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -25,6 +25,7 @@
"-Wthread-safety",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
],
@@ -33,6 +34,7 @@
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
+ "BufferReleaseChannel_test.cpp",
"Choreographer_test.cpp",
"CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 7d33f28..845a1ca 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -56,15 +56,14 @@
};
void SetUp() override {
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mBIC =
- new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true);
+ mBIC = new BufferItemConsumer(kFormat, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
mBFL = new BufferFreedListener(this);
mBIC->setBufferFreedListener(mBFL);
sp<IProducerListener> producerListener = new TrackingProducerListener(this);
+ mProducer = mBIC->getSurface()->getIGraphicBufferProducer();
IGraphicBufferProducer::QueueBufferOutput bufferOutput;
ASSERT_EQ(NO_ERROR,
mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 590e2c8..2e6ffcb 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1430,19 +1430,15 @@
}
struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
- BufferItemConsumerSetFrameRateListener(const sp<IGraphicBufferConsumer>& consumer)
- : BufferItemConsumer(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
+ BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
MOCK_METHOD(void, onSetFrameRate, (float, int8_t, int8_t), (override));
};
TEST_F(BufferQueueTest, TestSetFrameRate) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumerSetFrameRateListener> bufferConsumer =
- sp<BufferItemConsumerSetFrameRateListener>::make(consumer);
+ sp<BufferItemConsumerSetFrameRateListener>::make();
+ sp<IGraphicBufferProducer> producer = bufferConsumer->getSurface()->getIGraphicBufferProducer();
EXPECT_CALL(*bufferConsumer, onSetFrameRate(12.34f, 1, 0)).Times(1);
producer->setFrameRate(12.34f, 1, 0);
@@ -1493,14 +1489,10 @@
// See b/270004534
TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumer> bufferConsumer =
- sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2);
ASSERT_NE(nullptr, bufferConsumer.get());
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<Surface> surface = bufferConsumer->getSurface();
native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
native_window_set_buffers_dimensions(surface.get(), 100, 100);
@@ -1531,14 +1523,10 @@
}
TEST_F(BufferQueueTest, TestAdditionalOptions) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumer> bufferConsumer =
- sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2);
ASSERT_NE(nullptr, bufferConsumer.get());
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<Surface> surface = bufferConsumer->getSurface();
native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
native_window_set_buffers_dimensions(surface.get(), 100, 100);
diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp
new file mode 100644
index 0000000..11d122b
--- /dev/null
+++ b/libs/gui/tests/BufferReleaseChannel_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <gui/BufferReleaseChannel.h>
+
+using namespace std::string_literals;
+using android::gui::BufferReleaseChannel;
+
+namespace android {
+
+namespace {
+
+// Helper function to check if two file descriptors point to the same file.
+bool is_same_file(int fd1, int fd2) {
+ struct stat stat1;
+ if (fstat(fd1, &stat1) != 0) {
+ return false;
+ }
+ struct stat stat2;
+ if (fstat(fd2, &stat2) != 0) {
+ return false;
+ }
+ return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
+}
+
+} // namespace
+
+TEST(BufferReleaseChannelTest, MessageFlattenable) {
+ ReleaseCallbackId releaseCallbackId{1, 2};
+ sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
+ uint32_t maxAcquiredBufferCount = 5;
+
+ std::vector<uint8_t> dataBuffer;
+ std::vector<int> fdBuffer;
+
+ // Verify that we can flatten a message
+ {
+ BufferReleaseChannel::Message message{releaseCallbackId, releaseFence,
+ maxAcquiredBufferCount};
+
+ dataBuffer.resize(message.getFlattenedSize());
+ void* dataPtr = dataBuffer.data();
+ size_t dataSize = dataBuffer.size();
+
+ fdBuffer.resize(message.getFdCount());
+ int* fdPtr = fdBuffer.data();
+ size_t fdSize = fdBuffer.size();
+
+ ASSERT_EQ(OK, message.flatten(dataPtr, dataSize, fdPtr, fdSize));
+
+ // Fence's unique_fd uses fdsan to check ownership of the file descriptor. Normally the file
+ // descriptor is passed through the Unix socket and duplicated (and sent to another process)
+ // so there's no problem with duplicate file descriptor ownership. For this unit test, we
+ // need to set up a duplicate file descriptor to avoid crashing due to duplicate ownership.
+ ASSERT_EQ(releaseFence->get(), fdBuffer[0]);
+ fdBuffer[0] = message.releaseFence->dup();
+ }
+
+ // Verify that we can unflatten a message
+ {
+ BufferReleaseChannel::Message message;
+
+ const void* dataPtr = dataBuffer.data();
+ size_t dataSize = dataBuffer.size();
+
+ const int* fdPtr = fdBuffer.data();
+ size_t fdSize = fdBuffer.size();
+
+ ASSERT_EQ(OK, message.unflatten(dataPtr, dataSize, fdPtr, fdSize));
+ ASSERT_EQ(releaseCallbackId, message.releaseCallbackId);
+ ASSERT_TRUE(is_same_file(releaseFence->get(), message.releaseFence->get()));
+ ASSERT_EQ(maxAcquiredBufferCount, message.maxAcquiredBufferCount);
+ }
+}
+
+// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message
+// available.
+TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
+ ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
+
+ ReleaseCallbackId releaseCallbackId;
+ sp<Fence> releaseFence;
+ uint32_t maxAcquiredBufferCount;
+ ASSERT_EQ(WOULD_BLOCK,
+ consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
+}
+
+// Verify that we can write a message to the BufferReleaseChannel producer and read that message
+// using the BufferReleaseChannel consumer.
+TEST(BufferReleaseChannelTest, ProduceAndConsume) {
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
+ ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
+
+ sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
+
+ for (uint64_t i = 0; i < 64; i++) {
+ ReleaseCallbackId producerId{i, i + 1};
+ uint32_t maxAcquiredBufferCount = i + 2;
+ ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
+ }
+
+ for (uint64_t i = 0; i < 64; i++) {
+ ReleaseCallbackId expectedId{i, i + 1};
+ uint32_t expectedMaxAcquiredBufferCount = i + 2;
+
+ ReleaseCallbackId consumerId;
+ sp<Fence> consumerFence;
+ uint32_t maxAcquiredBufferCount;
+ ASSERT_EQ(OK,
+ consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
+
+ ASSERT_EQ(expectedId, consumerId);
+ ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get()));
+ ASSERT_EQ(expectedMaxAcquiredBufferCount, maxAcquiredBufferCount);
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index d80bd9c..f4239cb 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,13 +66,10 @@
test_info->name(),
params.width, params.height,
params.maxLockedBuffers, params.format);
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mCC = new CpuConsumer(consumer, params.maxLockedBuffers);
+ mCC = new CpuConsumer(params.maxLockedBuffers);
String8 name("CpuConsumer_Under_Test");
mCC->setName(name);
- mSTC = new Surface(producer);
+ mSTC = mCC->getSurface();
mANW = mSTC;
}
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 7d3d4aa..2428bb3 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,12 +34,8 @@
virtual void SetUp() {
GLTest::SetUp();
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mGlConsumer = new GLConsumer(consumer, TEX_ID,
- GLConsumer::TEXTURE_EXTERNAL, true, false);
- mSurface = new Surface(producer);
+ mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSurface = mGlConsumer->getSurface();
mANW = mSurface.get();
}
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b28dca8..59d05b6 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,12 +40,8 @@
}
virtual void SetUp() {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mST = new GLConsumer(consumer, 123, GLConsumer::TEXTURE_EXTERNAL, true,
- false);
- mSTC = new Surface(producer);
+ mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSTC = mST->getSurface();
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
@@ -731,12 +727,8 @@
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<GLConsumer> st(new GLConsumer(consumer, i,
- GLConsumer::TEXTURE_EXTERNAL, true, false));
- sp<Surface> stc(new Surface(producer));
+ sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false));
+ sp<Surface> stc = st->getSurface();
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 9d8af5d..1309635 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,11 +38,8 @@
void SetUp() {
GLTest::SetUp();
- sp<IGraphicBufferProducer> producer;
- BufferQueue::createBufferQueue(&producer, &mConsumer);
- mST = new GLConsumer(mConsumer, TEX_ID, GLConsumer::TEXTURE_EXTERNAL,
- true, false);
- mSTC = new Surface(producer);
+ mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSTC = mST->getSurface();
mANW = mSTC;
ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
@@ -63,7 +60,6 @@
mTextureRenderer->drawTexture();
}
- sp<IGraphicBufferConsumer> mConsumer;
sp<GLConsumer> mST;
sp<Surface> mSTC;
sp<ANativeWindow> mANW;
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index f76c0be..449533a 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -480,8 +480,8 @@
};
sp<DisconnectWaiter> dw(new DisconnectWaiter());
- mConsumer->consumerConnect(dw, false);
-
+ sp<IGraphicBufferConsumer> consumer = mST->getIGraphicBufferConsumer();
+ consumer->consumerConnect(dw, false);
sp<Thread> pt(new ProducerThread(mANW));
pt->run("ProducerThread");
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index ee0c555..8ab8783 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -151,10 +151,10 @@
if (hasSurfaceListener) {
listener = new FakeSurfaceListener(enableReleasedCb);
}
- ASSERT_EQ(OK, surface->connect(
- NATIVE_WINDOW_API_CPU,
- /*reportBufferRemoval*/true,
- /*listener*/listener));
+ ASSERT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU,
+ /*listener*/ listener,
+ /*reportBufferRemoval*/ true));
const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
@@ -272,13 +272,9 @@
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<BufferItemConsumer> c = new BufferItemConsumer(consumer,
- TEST_USAGE_FLAGS);
- sp<Surface> s = new Surface(producer);
+ sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS);
+ sp<Surface> s = c->getSurface();
sp<ANativeWindow> anw(s);
int flags = -1;
@@ -290,15 +286,11 @@
TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE);
- sp<Surface> s = new Surface(producer);
-
+ sp<Surface> s = cpuConsumer->getSurface();
sp<ANativeWindow> anw(s);
android_dataspace dataSpace;
@@ -311,11 +303,8 @@
}
TEST_F(SurfaceTest, SettingGenerationNumber) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
// Allocate a buffer with a generation number of 0
@@ -500,10 +489,10 @@
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
- ASSERT_EQ(OK, surface->connect(
- NATIVE_WINDOW_API_CPU,
- /*listener*/listener,
- /*reportBufferRemoval*/true));
+ ASSERT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU,
+ /*listener*/ listener,
+ /*reportBufferRemoval*/ true));
const int BUFFER_COUNT = 4;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
@@ -2155,12 +2144,9 @@
TEST_F(SurfaceTest, BatchOperations) {
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
@@ -2207,12 +2193,9 @@
TEST_F(SurfaceTest, BatchIllegalOperations) {
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
@@ -2237,12 +2220,8 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
TEST_F(SurfaceTest, PlatformBufferMethods) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
- sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(consumer, 1);
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
sp<GraphicBuffer> buffer;
sp<Fence> fence;
@@ -2294,13 +2273,9 @@
}
TEST_F(SurfaceTest, AllowAllocation) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
// controlledByApp must be true to disable blocking
- sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(consumer, 1, /*controlledByApp*/ true);
- sp<Surface> surface = sp<Surface>::make(producer, /*controlledByApp*/ true);
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1, /*controlledByApp*/ true);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
sp<GraphicBuffer> buffer;
sp<Fence> fence;
@@ -2374,7 +2349,7 @@
consumer->setFrameAvailableListener(consumerListener);
sp<DequeuingSurfaceListener> surfaceListener = sp<DequeuingSurfaceListener>::make(surface);
- EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, false, surfaceListener));
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener, false));
EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(2));
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8fbf5c6..e4e81ad 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -232,6 +232,7 @@
"MotionPredictorMetricsManager.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
+ "Resampler.cpp",
"TfLiteMotionPredictor.cpp",
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index c145d5c..99ffa68 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "InputTransport"
#define ATRACE_TAG ATRACE_TAG_INPUT
+#include <chrono>
+
#include <inttypes.h>
#include <android-base/logging.h>
@@ -168,6 +170,10 @@
return msg;
}
+bool isPointerEvent(const MotionEvent& motionEvent) {
+ return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
+}
+
} // namespace
using android::base::Result;
@@ -177,8 +183,13 @@
InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
sp<Looper> looper,
- InputConsumerCallbacks& callbacks)
- : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) {
+ InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler)
+ : mChannel(channel),
+ mLooper(looper),
+ mCallbacks(callbacks),
+ mResampler(std::move(resampler)),
+ mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
std::bind(&InputConsumerNoResampling::handleReceiveCallback, this,
@@ -463,6 +474,15 @@
}
messages.pop();
}
+ // Check if resampling should be performed.
+ if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) {
+ InputMessage* futureSample = nullptr;
+ if (!messages.empty()) {
+ futureSample = &messages.front();
+ }
+ mResampler->resampleMotionEvent(static_cast<std::chrono::nanoseconds>(frameTime),
+ *motionEvent, futureSample);
+ }
return std::make_pair(std::move(motionEvent), firstSeqForBatch);
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 5088188..f3241c9 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -240,8 +240,9 @@
std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const {
std::vector<int32_t> scanCodes;
+ // b/354333072: Only consider keys without FUNCTION flag
for (const auto& [scanCode, key] : mKeysByScanCode) {
- if (keyCode == key.keyCode) {
+ if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FUNCTION)) {
scanCodes.push_back(scanCode);
}
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 9c70535..c61d394 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -72,7 +72,8 @@
// --- JerkTracker ---
-JerkTracker::JerkTracker(bool normalizedDt) : mNormalizedDt(normalizedDt) {}
+JerkTracker::JerkTracker(bool normalizedDt, float alpha)
+ : mNormalizedDt(normalizedDt), mAlpha(alpha) {}
void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) {
// If we previously had full samples, we have a previous jerk calculation
@@ -122,7 +123,7 @@
float newJerkMagnitude = std::hypot(newXDerivatives[3], newYDerivatives[3]);
ALOGD_IF(isDebug(), "raw jerk: %f", newJerkMagnitude);
if (applySmoothing) {
- mJerkMagnitude = mJerkMagnitude + (mForgetFactor * (newJerkMagnitude - mJerkMagnitude));
+ mJerkMagnitude = mJerkMagnitude + (mAlpha * (newJerkMagnitude - mJerkMagnitude));
} else {
mJerkMagnitude = newJerkMagnitude;
}
@@ -143,14 +144,6 @@
return std::nullopt;
}
-void JerkTracker::setForgetFactor(float forgetFactor) {
- mForgetFactor = forgetFactor;
-}
-
-float JerkTracker::getForgetFactor() const {
- return mForgetFactor;
-}
-
// --- MotionPredictor ---
MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
@@ -160,6 +153,24 @@
mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)),
mReportAtomFunction(reportAtomFunction) {}
+void MotionPredictor::initializeObjects() {
+ mModel = TfLiteMotionPredictorModel::create();
+ LOG_ALWAYS_FATAL_IF(!mModel);
+
+ // mJerkTracker assumes normalized dt = 1 between recorded samples because
+ // the underlying mModel input also assumes fixed-interval samples.
+ // Normalized dt as 1 is also used to correspond with the similar Jank
+ // implementation from the JetPack MotionPredictor implementation.
+ mJerkTracker = std::make_unique<JerkTracker>(/*normalizedDt=*/true, mModel->config().jerkAlpha);
+
+ mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+
+ mMetricsManager =
+ std::make_unique<MotionPredictorMetricsManager>(mModel->config().predictionInterval,
+ mModel->outputLength(),
+ mReportAtomFunction);
+}
+
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
// We still have an active gesture for another device. The provided MotionEvent is not
@@ -176,29 +187,18 @@
return {};
}
- // Initialise the model now that it's likely to be used.
if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create();
- LOG_ALWAYS_FATAL_IF(!mModel);
- mJerkTracker.setForgetFactor(mModel->config().jerkForgetFactor);
- }
-
- if (!mBuffers) {
- mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+ initializeObjects();
}
// Pass input event to the MetricsManager.
- if (!mMetricsManager) {
- mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength(),
- mReportAtomFunction);
- }
mMetricsManager->onRecord(event);
const int32_t action = event.getActionMasked();
if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
mBuffers->reset();
- mJerkTracker.reset();
+ mJerkTracker->reset();
mLastEvent.reset();
return {};
} else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
@@ -233,9 +233,9 @@
0, i),
.orientation = event.getHistoricalOrientation(0, i),
});
- mJerkTracker.pushSample(event.getHistoricalEventTime(i),
- coords->getAxisValue(AMOTION_EVENT_AXIS_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ mJerkTracker->pushSample(event.getHistoricalEventTime(i),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
}
if (!mLastEvent) {
@@ -283,7 +283,7 @@
int64_t predictionTime = mBuffers->lastTimestamp();
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
- const float jerkMagnitude = mJerkTracker.jerkMagnitude().value_or(0);
+ const float jerkMagnitude = mJerkTracker->jerkMagnitude().value_or(0);
const float fractionKept =
1 - normalizeRange(jerkMagnitude, mModel->config().lowJerk, mModel->config().highJerk);
// float to ensure proper division below.
@@ -379,12 +379,4 @@
return true;
}
-const TfLiteMotionPredictorModel::Config& MotionPredictor::getModelConfig() {
- if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create();
- LOG_ALWAYS_FATAL_IF(!mModel);
- }
- return mModel->config();
-}
-
} // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
new file mode 100644
index 0000000..af8354c
--- /dev/null
+++ b/libs/input/Resampler.cpp
@@ -0,0 +1,151 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LegacyResampler"
+
+#include <algorithm>
+#include <chrono>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include <input/Resampler.h>
+#include <utils/Timers.h>
+
+using std::chrono::nanoseconds;
+
+namespace android {
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+bool debugResampling() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
+ ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_RESAMPLING;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+}
+
+constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
+constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
+
+inline float lerp(float a, float b, float alpha) {
+ return a + alpha * (b - a);
+}
+
+const PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
+ const float alpha) {
+ // Ensure the struct PointerCoords is initialized.
+ PointerCoords resampledCoords{};
+ resampledCoords.isResampled = true;
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
+ return resampledCoords;
+}
+} // namespace
+
+void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
+ const size_t motionEventSampleSize = motionEvent.getHistorySize() + 1;
+ for (size_t i = 0; i < motionEventSampleSize; ++i) {
+ Sample sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
+ *motionEvent.getPointerProperties(0),
+ motionEvent.getSamplePointerCoords()[i]};
+ mLatestSamples.pushBack(sample);
+ }
+}
+
+void LegacyResampler::interpolate(const nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage& futureSample) const {
+ const Sample pastSample = mLatestSamples.back();
+ const nanoseconds delta =
+ static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return;
+ }
+ const float alpha =
+ std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
+
+ const PointerCoords resampledCoords =
+ calculateResampledCoords(pastSample.pointer.coords,
+ futureSample.body.motion.pointers[0].coords, alpha);
+ motionEvent.addSample(resampleTime.count(), &resampledCoords, motionEvent.getId());
+}
+
+void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& motionEvent) const {
+ if (mLatestSamples.size() < 2) {
+ return;
+ }
+ const Sample pastSample = *(mLatestSamples.end() - 2);
+ const Sample presentSample = *(mLatestSamples.end() - 1);
+ const nanoseconds delta =
+ static_cast<nanoseconds>(presentSample.eventTime - pastSample.eventTime);
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return;
+ } else if (delta > RESAMPLE_MAX_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
+ return;
+ }
+ // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
+ // we use this value as the resample time target.
+ const nanoseconds farthestPrediction = static_cast<nanoseconds>(presentSample.eventTime) +
+ std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
+ const nanoseconds newResampleTime =
+ (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
+ LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
+ << "Resample time is too far in the future. Adjusting prediction from "
+ << (resampleTime - presentSample.eventTime) << " to "
+ << (farthestPrediction - presentSample.eventTime) << "ns.";
+ const float alpha =
+ std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
+ delta;
+
+ const PointerCoords resampledCoords =
+ calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
+ alpha);
+ motionEvent.addSample(newResampleTime.count(), &resampledCoords, motionEvent.getId());
+}
+
+void LegacyResampler::resampleMotionEvent(const nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) {
+ if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
+ mLatestSamples.clear();
+ }
+ mPreviousDeviceId = motionEvent.getDeviceId();
+ updateLatestSamples(motionEvent);
+ if (futureSample) {
+ interpolate(resampleTime, motionEvent, *futureSample);
+ } else {
+ extrapolate(resampleTime, motionEvent);
+ }
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
+}
+} // namespace android
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index b401c98..5250a9d 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -283,7 +283,7 @@
.distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
.lowJerk = parseXMLFloat(*configRoot, "low-jerk"),
.highJerk = parseXMLFloat(*configRoot, "high-jerk"),
- .jerkForgetFactor = parseXMLFloat(*configRoot, "jerk-forget-factor"),
+ .jerkAlpha = parseXMLFloat(*configRoot, "jerk-alpha"),
};
return std::unique_ptr<TfLiteMotionPredictorModel>(
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index 0579967..51edbf1 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "VirtualInputDevice"
+#include <android-base/logging.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android_companion_virtualdevice_flags.h>
@@ -23,26 +24,259 @@
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
#include <linux/uinput.h>
-#include <math.h>
-#include <utils/Log.h>
-#include <map>
#include <string>
using android::base::unique_fd;
+namespace {
+
/**
* Log debug messages about native virtual input devices.
* Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG"
*/
-static bool isDebug() {
+bool isDebug() {
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
}
+unique_fd invalidFd() {
+ return unique_fd(-1);
+}
+
+} // namespace
+
namespace android {
namespace vd_flags = android::companion::virtualdevice::flags;
+/** Creates a new uinput device and assigns a file descriptor. */
+unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth) {
+ unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
+ if (fd < 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+
+ ioctl(fd, UI_SET_PHYS, phys);
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+ switch (deviceType) {
+ case DeviceType::DPAD:
+ for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::KEYBOARD:
+ for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::MOUSE:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
+ ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
+ ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
+ ioctl(fd, UI_SET_RELBIT, REL_X);
+ ioctl(fd, UI_SET_RELBIT, REL_Y);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
+ }
+ break;
+ case DeviceType::TOUCHSCREEN:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ break;
+ case DeviceType::STYLUS:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
+ ioctl(fd, UI_SET_ABSBIT, ABS_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ break;
+ case DeviceType::ROTARY_ENCODER:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
+ }
+ break;
+ default:
+ ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
+ return invalidFd();
+ }
+
+ int version;
+ if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
+ uinput_setup setup;
+ memset(&setup, 0, sizeof(setup));
+ std::strncpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
+ setup.id.version = 1;
+ setup.id.bustype = BUS_VIRTUAL;
+ setup.id.vendor = vendorId;
+ setup.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_MT_POSITION_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_MT_POSITION_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup majorAbsSetup;
+ majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
+ majorAbsSetup.absinfo.maximum = screenWidth - 1;
+ majorAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_MT_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup slotAbsSetup;
+ slotAbsSetup.code = ABS_MT_SLOT;
+ slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ slotAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup trackingIdAbsSetup;
+ trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
+ trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ trackingIdAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
+ return invalidFd();
+ }
+ } else if (deviceType == DeviceType::STYLUS) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup tiltXAbsSetup;
+ tiltXAbsSetup.code = ABS_TILT_X;
+ tiltXAbsSetup.absinfo.maximum = 90;
+ tiltXAbsSetup.absinfo.minimum = -90;
+ if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup tiltYAbsSetup;
+ tiltYAbsSetup.code = ABS_TILT_Y;
+ tiltYAbsSetup.absinfo.maximum = 90;
+ tiltYAbsSetup.absinfo.minimum = -90;
+ if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ }
+ if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+ } else {
+ // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
+ ALOGI("Falling back to version %d manual setup", version);
+ uinput_user_dev fallback;
+ memset(&fallback, 0, sizeof(fallback));
+ std::strncpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
+ fallback.id.version = 1;
+ fallback.id.bustype = BUS_VIRTUAL;
+ fallback.id.vendor = vendorId;
+ fallback.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ fallback.absmin[ABS_MT_POSITION_X] = 0;
+ fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
+ fallback.absmin[ABS_MT_POSITION_Y] = 0;
+ fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
+ fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
+ fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
+ fallback.absmin[ABS_MT_PRESSURE] = 0;
+ fallback.absmax[ABS_MT_PRESSURE] = 255;
+ } else if (deviceType == DeviceType::STYLUS) {
+ fallback.absmin[ABS_X] = 0;
+ fallback.absmax[ABS_X] = screenWidth - 1;
+ fallback.absmin[ABS_Y] = 0;
+ fallback.absmax[ABS_Y] = screenHeight - 1;
+ fallback.absmin[ABS_TILT_X] = -90;
+ fallback.absmax[ABS_TILT_X] = 90;
+ fallback.absmin[ABS_TILT_Y] = -90;
+ fallback.absmax[ABS_TILT_Y] = 90;
+ fallback.absmin[ABS_PRESSURE] = 0;
+ fallback.absmax[ABS_PRESSURE] = 255;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+ }
+
+ if (ioctl(fd, UI_DEV_CREATE) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+
+ return fd;
+}
+
VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
VirtualInputDevice::~VirtualInputDevice() {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e9d799e..132866b 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -23,6 +23,7 @@
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
+ "Resampler_test.cpp",
"RingBuffer_test.cpp",
"TfLiteMotionPredictor_test.cpp",
"TouchResampling_test.cpp",
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index e710613..467c3b4 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -396,8 +396,9 @@
break;
}
case LooperMessage::CREATE_CONSUMER: {
- mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel),
- mLooper, *this);
+ mConsumer =
+ std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), mLooper,
+ *this, /*resampler=*/nullptr);
break;
}
case LooperMessage::DESTROY_CONSUMER: {
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 5bd5794..106e686 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -70,7 +70,7 @@
}
TEST(JerkTrackerTest, JerkReadiness) {
- JerkTracker jerkTracker(true);
+ JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1);
EXPECT_FALSE(jerkTracker.jerkMagnitude());
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
EXPECT_FALSE(jerkTracker.jerkMagnitude());
@@ -87,8 +87,8 @@
}
TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) {
- JerkTracker jerkTracker(true);
- jerkTracker.setForgetFactor(.5);
+ const float alpha = .5;
+ JerkTracker jerkTracker(/*normalizedDt=*/true, alpha);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -119,14 +119,13 @@
* y'': 3 -> -15
* y''': -18
*/
- const float newJerk = (1 - jerkTracker.getForgetFactor()) * std::hypot(10, -1) +
- jerkTracker.getForgetFactor() * std::hypot(-50, -18);
+ const float newJerk = (1 - alpha) * std::hypot(10, -1) + alpha * std::hypot(-50, -18);
EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
}
TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) {
- JerkTracker jerkTracker(false);
- jerkTracker.setForgetFactor(.5);
+ const float alpha = .5;
+ JerkTracker jerkTracker(/*normalizedDt=*/false, alpha);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/10, 25, 53);
jerkTracker.pushSample(/*timestamp=*/20, 30, 60);
@@ -157,13 +156,12 @@
* y'': .03 -> -.125 (delta above, divide by 10)
* y''': -.0155 (delta above, divide by 10)
*/
- const float newJerk = (1 - jerkTracker.getForgetFactor()) * std::hypot(.01, -.001) +
- jerkTracker.getForgetFactor() * std::hypot(-.0375, -.0155);
+ const float newJerk = (1 - alpha) * std::hypot(.01, -.001) + alpha * std::hypot(-.0375, -.0155);
EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
}
TEST(JerkTrackerTest, JerkCalculationAfterReset) {
- JerkTracker jerkTracker(true);
+ JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -297,8 +295,11 @@
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
+ // Create another instance of TfLiteMotionPredictorModel to read config details.
+ std::unique_ptr<TfLiteMotionPredictorModel> testTfLiteModel =
+ TfLiteMotionPredictorModel::create();
const float mediumJerk =
- (predictor.getModelConfig().lowJerk + predictor.getModelConfig().highJerk) / 2;
+ (testTfLiteModel->config().lowJerk + testTfLiteModel->config().highJerk) / 2;
const float a = 3; // initial acceleration
const float b = 4; // initial velocity
const float c = 5; // initial position
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
new file mode 100644
index 0000000..e160ca0
--- /dev/null
+++ b/libs/input/tests/Resampler_test.cpp
@@ -0,0 +1,417 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/Resampler.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/InputTransport.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::literals::chrono_literals;
+
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+ /**
+ * Converts from Pointer to PointerCoords. Enables calling LegacyResampler methods and
+ * assertions only with the relevant data for tests.
+ */
+ operator PointerCoords() const;
+};
+
+Pointer::operator PointerCoords() const {
+ PointerCoords pointerCoords;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ pointerCoords.isResampled = isResampled;
+ return pointerCoords;
+}
+
+struct InputSample {
+ std::chrono::milliseconds eventTime{0};
+ std::vector<Pointer> pointers{};
+ /**
+ * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator InputMessage() const;
+};
+
+InputSample::operator InputMessage() const {
+ InputMessage message;
+ message.header.type = InputMessage::Type::MOTION;
+ message.body.motion.pointerCount = pointers.size();
+ message.body.motion.eventTime = static_cast<std::chrono::nanoseconds>(eventTime).count();
+ message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
+ message.body.motion.downTime = 0;
+ const uint32_t pointerCount = message.body.motion.pointerCount;
+ for (uint32_t i = 0; i < pointerCount; ++i) {
+ message.body.motion.pointers[i].properties.id = pointers[i].id;
+ message.body.motion.pointers[i].properties.toolType = pointers[i].toolType;
+ message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
+ message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
+ message.body.motion.pointers[i].coords.isResampled = pointers[i].isResampled;
+ }
+ return message;
+}
+
+struct InputStream {
+ std::vector<InputSample> samples{};
+ int32_t action{0};
+ DeviceId deviceId{0};
+ /**
+ * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator MotionEvent() const;
+};
+
+InputStream::operator MotionEvent() const {
+ const InputSample& firstSample{*samples.begin()};
+ MotionEventBuilder motionEventBuilder =
+ MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
+ .downTime(0)
+ .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count())
+ .deviceId(deviceId);
+ for (const Pointer& pointer : firstSample.pointers) {
+ const PointerBuilder pointerBuilder =
+ PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y);
+ motionEventBuilder.pointer(pointerBuilder);
+ }
+ MotionEvent motionEvent = motionEventBuilder.build();
+ const size_t numSamples = samples.size();
+ for (size_t i = 1; i < numSamples; ++i) {
+ std::vector<PointerCoords> pointersCoords{samples[i].pointers.begin(),
+ samples[i].pointers.end()};
+ motionEvent.addSample(static_cast<std::chrono::nanoseconds>(samples[i].eventTime).count(),
+ pointersCoords.data(), motionEvent.getId());
+ }
+ return motionEvent;
+}
+
+} // namespace
+
+class ResamplerTest : public testing::Test {
+protected:
+ ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {}
+
+ ~ResamplerTest() override {}
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ std::unique_ptr<Resampler> mResampler;
+
+ MotionEvent buildMotionEvent(const int32_t action, const nsecs_t eventTime,
+ const std::vector<PointerBuilder>& pointers);
+
+ InputMessage createMessage(const uint32_t pointerCount, const nsecs_t eventTime,
+ const int32_t action,
+ const std::vector<PointerProperties>& properties,
+ const std::vector<PointerCoords>& coords);
+
+ /**
+ * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample
+ * member function.
+ * @param beforeCall MotionEvent before passing it to resampleMotionEvent
+ * @param afterCall MotionEvent after passing it to resampleMotionEvent
+ */
+ void assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall);
+
+ /**
+ * Asserts the MotionEvent is resampled by checking an increment in history size and that the
+ * resampled coordinates are near the expected ones.
+ */
+ void assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original,
+ const MotionEvent& resampled,
+ const PointerCoords& expectedCoords);
+
+ void assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled);
+};
+
+MotionEvent ResamplerTest::buildMotionEvent(const int32_t action, const nsecs_t eventTime,
+ const std::vector<PointerBuilder>& pointerBuilders) {
+ MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
+ .downTime(0)
+ .eventTime(eventTime);
+ for (const PointerBuilder& pointerBuilder : pointerBuilders) {
+ motionEventBuilder.pointer(pointerBuilder);
+ }
+ return motionEventBuilder.build();
+}
+
+InputMessage ResamplerTest::createMessage(const uint32_t pointerCount, const nsecs_t eventTime,
+ const int32_t action,
+ const std::vector<PointerProperties>& properties,
+ const std::vector<PointerCoords>& coords) {
+ InputMessage message;
+ message.header.type = InputMessage::Type::MOTION;
+ message.body.motion.pointerCount = pointerCount;
+ message.body.motion.eventTime = eventTime;
+ message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
+ message.body.motion.downTime = 0;
+ for (uint32_t i = 0; i < pointerCount; ++i) {
+ message.body.motion.pointers[i].properties = properties[i];
+ message.body.motion.pointers[i].coords = coords[i];
+ }
+ return message;
+}
+
+void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall) {
+ EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId());
+ EXPECT_EQ(beforeCall.getAction(), afterCall.getAction());
+ EXPECT_EQ(beforeCall.getActionButton(), afterCall.getActionButton());
+ EXPECT_EQ(beforeCall.getButtonState(), afterCall.getButtonState());
+ EXPECT_EQ(beforeCall.getFlags(), afterCall.getFlags());
+ EXPECT_EQ(beforeCall.getEdgeFlags(), afterCall.getEdgeFlags());
+ EXPECT_EQ(beforeCall.getClassification(), afterCall.getClassification());
+ EXPECT_EQ(beforeCall.getPointerCount(), afterCall.getPointerCount());
+ EXPECT_EQ(beforeCall.getMetaState(), afterCall.getMetaState());
+ EXPECT_EQ(beforeCall.getSource(), afterCall.getSource());
+ EXPECT_EQ(beforeCall.getXPrecision(), afterCall.getXPrecision());
+ EXPECT_EQ(beforeCall.getYPrecision(), afterCall.getYPrecision());
+ EXPECT_EQ(beforeCall.getDownTime(), afterCall.getDownTime());
+ EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId());
+}
+
+void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original,
+ const MotionEvent& resampled,
+ const PointerCoords& expectedCoords) {
+ assertMotionEventMetaDataDidNotMutate(original, resampled);
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t resampledSampleSize = resampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize + 1, resampledSampleSize);
+ const PointerCoords& resampledCoords =
+ resampled.getSamplePointerCoords()[resampled.getHistorySize()];
+ EXPECT_TRUE(resampledCoords.isResampled);
+ EXPECT_NEAR(expectedCoords.getX(), resampledCoords.getX(), EPSILON);
+ EXPECT_NEAR(expectedCoords.getY(), resampledCoords.getY(), EPSILON);
+}
+
+void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled) {
+ assertMotionEventMetaDataDidNotMutate(original, notResampled);
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t notResampledSampleSize = notResampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize, notResampledSampleSize);
+}
+
+TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 0};
+ const MotionEvent originalMotionEvent = motionEvent;
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) {
+ MotionEvent motionFromFirstDevice =
+ InputStream{{{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 0};
+ mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr);
+ MotionEvent motionFromSecondDevice =
+ InputStream{{{11ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 1};
+ const MotionEvent originalMotionEvent = motionFromSecondDevice;
+ mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr);
+ // The MotionEvent should not be resampled because the second event came from a different device
+ // than the previous event.
+ assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice);
+}
+
+// Increments of 16 ms for display refresh rate
+// Increments of 6 ms for input frequency
+// Resampling latency is known to be 5 ms
+// Therefore, first resampling time will be 11 ms
+
+/**
+ * Timeline
+ * ----+----------------------+---------+---------+---------+----------
+ * 0ms 10ms 11ms 15ms 16ms
+ * DOWN MOVE | MSG |
+ * resample frame
+ * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available
+ * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame
+ * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between
+ * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the
+ * last one in the batch to consume.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 1.2f,
+ .y = 1.2f,
+ .isResampled = true});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(10'500'000ns, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+/**
+ * Tests extrapolation given two MotionEvents with a single sample.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) {
+ MotionEvent previousMotionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(10ms, previousMotionEvent, nullptr);
+
+ MotionEvent motionEvent =
+ InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = true});
+ // Integrity of the whole motionEvent
+ // History size should increment by 1
+ // Check if the resampled value is the last one
+ // Check if the resampleTime is correct
+ // Check if the PointerCoords are consistent with the other computations
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.2f,
+ .isResampled = true});
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.2f,
+ .isResampled = true});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{9ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {26ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(27ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ {25ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(43ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ Pointer{.id = 0,
+ .x = 2.4f,
+ .y = 2.4f,
+ .isResampled = true});
+}
+} // namespace android
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 0dac662..7278d2d 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -620,15 +620,10 @@
} // namespace details
} // namespace android
-namespace std {
- template<template<typename T> class VECTOR, typename T>
- struct hash<VECTOR<T>> {
- static constexpr bool IS_VECTOR =
- std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
-
- typename std::enable_if<IS_VECTOR, size_t>::type
- operator()(const VECTOR<T>& v) const {
- return v.hash();
- }
- };
-}
+#define TVECHELPERS_STD_HASH(VECTOR) \
+ template <typename T> \
+ struct std::hash<VECTOR<T>> { \
+ size_t operator()(const VECTOR<T>& v) const { \
+ return v.hash(); \
+ } \
+ }
diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h
index 3e6cd4c..24c2bad 100644
--- a/libs/math/include/math/mat2.h
+++ b/libs/math/include/math/mat2.h
@@ -373,5 +373,7 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TMat22);
+
#undef PURE
#undef CONSTEXPR
diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h
index 5c8a9b2..4647a60 100644
--- a/libs/math/include/math/mat3.h
+++ b/libs/math/include/math/mat3.h
@@ -436,5 +436,7 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TMat33);
+
#undef PURE
#undef CONSTEXPR
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
index c630d97..c9e118a 100644
--- a/libs/math/include/math/mat4.h
+++ b/libs/math/include/math/mat4.h
@@ -590,5 +590,7 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TMat44);
+
#undef PURE
#undef CONSTEXPR
diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h
index 07573c5..43c8038 100644
--- a/libs/math/include/math/quat.h
+++ b/libs/math/include/math/quat.h
@@ -187,6 +187,8 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TQuaternion);
+
#pragma clang diagnostic pop
#undef PURE
diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h
index e0adb7f..909c77e 100644
--- a/libs/math/include/math/vec2.h
+++ b/libs/math/include/math/vec2.h
@@ -122,4 +122,6 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TVec2);
+
#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h
index 21fb684..ff2b3e4 100644
--- a/libs/math/include/math/vec3.h
+++ b/libs/math/include/math/vec3.h
@@ -128,4 +128,6 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TVec3);
+
#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h
index 1e279fe..16509c9 100644
--- a/libs/math/include/math/vec4.h
+++ b/libs/math/include/math/vec4.h
@@ -125,4 +125,6 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TVec4);
+
#pragma clang diagnostic pop
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index dd78049..ca41346 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -196,10 +196,10 @@
return BAD_VALUE;
}
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -248,10 +248,10 @@
if (!buffer) return BAD_VALUE;
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -277,10 +277,10 @@
int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) {
if (!buffer || !outPlanes) return BAD_VALUE;
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index ecf98c6..7639fab 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -101,6 +101,7 @@
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
"skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
"skia/filters/MouriMap.cpp",
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 05a2063..326d1ce 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -64,14 +64,15 @@
return std::pair<uint32_t, uint32_t>(width, height);
}
-static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded,
- RenderEngine::GraphicsApi graphicsApi) {
+static std::unique_ptr<RenderEngine> createRenderEngine(
+ RenderEngine::Threaded threaded, RenderEngine::GraphicsApi graphicsApi,
+ RenderEngine::BlurAlgorithm blurAlgorithm = RenderEngine::BlurAlgorithm::KAWASE) {
auto args = RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
.setEnableProtectedContext(true)
.setPrecacheToneMapperShaderOnly(false)
- .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE)
+ .setBlurAlgorithm(blurAlgorithm)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
.setThreaded(threaded)
.setGraphicsApi(graphicsApi)
@@ -180,7 +181,8 @@
void BM_blur(benchmark::State& benchState, Args&&... args) {
auto args_tuple = std::make_tuple(std::move(args)...);
auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
- static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
+ static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)),
+ static_cast<RenderEngine::BlurAlgorithm>(std::get<2>(args_tuple)));
// Initially use cpu access so we can decode into it with AImageDecoder.
auto [width, height] = getDisplaySize();
@@ -224,5 +226,11 @@
benchDrawLayers(*re, layers, benchState, "blurred");
}
-BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES,
- RenderEngine::GraphicsApi::GL);
+BENCHMARK_CAPTURE(BM_blur, gaussian, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL,
+ RenderEngine::BlurAlgorithm::GAUSSIAN);
+
+BENCHMARK_CAPTURE(BM_blur, kawase, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL,
+ RenderEngine::BlurAlgorithm::KAWASE);
+
+BENCHMARK_CAPTURE(BM_blur, kawase_dual_filter, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 7207394..9bc2c48 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -131,6 +131,7 @@
NONE,
GAUSSIAN,
KAWASE,
+ KAWASE_DUAL_FILTER,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
index b5cb21b..390ad6e 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -23,6 +23,7 @@
#include <include/gpu/graphite/BackendSemaphore.h>
#include <include/gpu/graphite/Context.h>
#include <include/gpu/graphite/Recording.h>
+#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
#include <log/log_main.h>
#include <sync/sync.h>
@@ -77,7 +78,7 @@
base::unique_fd fenceDup(dupedFd);
VkSemaphore waitSemaphore =
getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
- graphite::BackendSemaphore beSemaphore(waitSemaphore);
+ auto beSemaphore = graphite::BackendSemaphores::MakeVulkan(waitSemaphore);
mStagedWaitSemaphores.push_back(beSemaphore);
}
@@ -92,7 +93,7 @@
// This "signal" semaphore is called after rendering, but it is cleaned up in the same mechanism
// as "wait" semaphores from waitFence.
VkSemaphore vkSignalSemaphore = vulkanInterface.createExportableSemaphore();
- graphite::BackendSemaphore backendSignalSemaphore(vkSignalSemaphore);
+ auto backendSignalSemaphore = graphite::BackendSemaphores::MakeVulkan(vkSignalSemaphore);
// Collect all Vk semaphores that DestroySemaphoreInfo needs to own and delete after GPU work.
std::vector<VkSemaphore> vkSemaphoresToCleanUp;
@@ -100,7 +101,8 @@
vkSemaphoresToCleanUp.push_back(vkSignalSemaphore);
}
for (auto backendWaitSemaphore : mStagedWaitSemaphores) {
- vkSemaphoresToCleanUp.push_back(backendWaitSemaphore.getVkSemaphore());
+ vkSemaphoresToCleanUp.push_back(
+ graphite::BackendSemaphores::GetVkSemaphore(backendWaitSemaphore));
}
DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 9709cd1..d58f303 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -76,6 +76,7 @@
#include "compat/SkiaGpuContext.h"
#include "filters/BlurFilter.h"
#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurDualFilter.h"
#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "filters/MouriMap.h"
@@ -285,6 +286,11 @@
mBlurFilter = new KawaseBlurFilter();
break;
}
+ case BlurAlgorithm::KAWASE_DUAL_FILTER: {
+ ALOGD("Background Blurs Enabled (Kawase dual-filtering algorithm)");
+ mBlurFilter = new KawaseBlurDualFilter();
+ break;
+ }
default: {
mBlurFilter = nullptr;
break;
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
new file mode 100644
index 0000000..db0b133
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurDualFilter.h"
+#include <SkAlphaType.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() {
+ // A shader to sample each vertex of a unit regular heptagon
+ // plus the original fragment coordinate.
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+ uniform float in_crossFade;
+
+ const float2 STEP_0 = float2( 1.0, 0.0);
+ const float2 STEP_1 = float2( 0.623489802, 0.781831482);
+ const float2 STEP_2 = float2(-0.222520934, 0.974927912);
+ const float2 STEP_3 = float2(-0.900968868, 0.433883739);
+ const float2 STEP_4 = float2( 0.900968868, -0.433883739);
+ const float2 STEP_5 = float2(-0.222520934, -0.974927912);
+ const float2 STEP_6 = float2(-0.623489802, -0.781831482);
+
+ half4 main(float2 xy) {
+ half3 c = child.eval(xy).rgb;
+
+ c += child.eval(xy + STEP_0 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_1 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_2 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_3 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_4 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_5 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_6 * in_blurOffset).rgb;
+
+ return half4(c * 0.125 * in_crossFade, in_crossFade);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ LOG_ALWAYS_FATAL_IF(!blurEffect, "RuntimeShader error: %s", error.c_str());
+ mBlurEffect = std::move(blurEffect);
+}
+
+static sk_sp<SkSurface> makeSurface(SkiaGpuContext* context, const SkRect& origRect, int scale) {
+ SkImageInfo scaledInfo =
+ SkImageInfo::MakeN32Premul(ceil(static_cast<float>(origRect.width()) / scale),
+ ceil(static_cast<float>(origRect.height()) / scale));
+ return context->createRenderTarget(scaledInfo);
+}
+
+void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface,
+ const sk_sp<SkImage>& readImage, const float radius,
+ const float alpha) const {
+ const float scale = static_cast<float>(drawSurface->width()) / readImage->width();
+ SkMatrix blurMatrix = SkMatrix::Scale(scale, scale);
+ blurInto(drawSurface,
+ readImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
+ blurMatrix),
+ readImage->width() / static_cast<float>(drawSurface->width()), radius, alpha);
+}
+
+void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input,
+ const float inverseScale, const float radius,
+ const float alpha) const {
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") = std::move(input);
+ blurBuilder.uniform("in_inverseScale") = inverseScale;
+ blurBuilder.uniform("in_blurOffset") = radius;
+ blurBuilder.uniform("in_crossFade") = alpha;
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr));
+ paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
+ drawSurface->getCanvas()->drawPaint(paint);
+}
+
+sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input,
+ const SkRect& blurRect) const {
+ // Apply a conversion factor of (1 / sqrt(3)) to match Skia's built-in blur as used by
+ // RenderEffect. See the comment in SkBlurMask.cpp for reasoning behind this.
+ const float radius = blurRadius * 0.57735f;
+
+ // Use a variable number of blur passes depending on the radius. The non-integer part of this
+ // calculation is used to mix the final pass into the second-last with an alpha blend.
+ constexpr int kMaxSurfaces = 4;
+ const float filterDepth =
+ std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale)));
+ const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));
+
+ // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale.
+ sk_sp<SkSurface> surfaces[kMaxSurfaces] =
+ {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
+ filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
+ filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
+ filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
+
+ // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600.
+ static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f};
+
+ // Kawase is an approximation of Gaussian, but behaves differently because it is made up of many
+ // simpler blurs. A transformation is required to approximate the same effect as Gaussian.
+ float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f);
+ for (int i = 0; i < filterPasses; i++) {
+ const float alpha = std::min(1.0f, filterDepth - i);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f);
+ }
+ // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets.
+ const float step = M_SQRT1_2 *
+ sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR));
+
+ // Start by downscaling and doing the first blur pass.
+ {
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case one may expect Translate(blurRect.fLeft, blurRect.fTop) * Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+ const auto sourceShader =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
+ blurMatrix);
+ blurInto(surfaces[0], std::move(sourceShader), kInputScale, kWeights[0] * step, 1.0f);
+ }
+ // Next the remaining downscale blur passes.
+ for (int i = 0; i < filterPasses; i++) {
+ blurInto(surfaces[i + 1], surfaces[i]->makeImageSnapshot(), kWeights[1 + i] * step, 1.0f);
+ }
+ // Finally blur+upscale back to our original size.
+ for (int i = filterPasses - 1; i >= 0; i--) {
+ blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step,
+ std::min(1.0f, filterDepth - i));
+ }
+ return surfaces[0]->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
new file mode 100644
index 0000000..6f4adbf
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+#include "BlurFilter.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur with dual-filtering passes, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_slides.pdf
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurDualFilter : public BlurFilter {
+public:
+ explicit KawaseBlurDualFilter();
+ virtual ~KawaseBlurDualFilter() {}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+
+ void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkImage>& readImage,
+ const float radius, const float alpha) const;
+
+ void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkShader> input,
+ const float inverseScale, const float radius, const float alpha) const;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 84852ea..bec9255 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -42,10 +42,11 @@
// ----------------------------------------------------------------------------
SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection,
- SensorManager& sensorManager)
+ SensorManager& sensorManager, String8 packageName)
: mSensorEventConnection(connection),
mRecBuffer(nullptr),
mSensorManager(sensorManager),
+ mPackageName(packageName),
mAvailable(0),
mConsumed(0),
mNumAcksToSend(0) {
@@ -92,8 +93,11 @@
mSensorManager.getSensorNameByHandle(events->sensor);
if (sensorName.has_value()) {
char buffer[UINT8_MAX];
- std::snprintf(buffer, sizeof(buffer), "Sensor event from %s",
- sensorName.value().data());
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ std::snprintf(buffer, sizeof(buffer),
+ "Sensor event from %s to %s PID: %d (%zu/%zu)",
+ sensorName.value().data(), mPackageName.c_str(), pid, i, count);
ATRACE_INSTANT_FOR_TRACK(LOG_TAG, buffer);
}
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 3ca6f0f..5673678 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -73,8 +73,6 @@
return deviceId;
}
}
- } else {
- ALOGW("Cannot get virtualdevice_native service");
}
return DEVICE_ID_DEFAULT;
}
@@ -403,7 +401,7 @@
ALOGE("createEventQueue: connection is NULL.");
return nullptr;
}
- queue = new SensorEventQueue(connection, *this);
+ queue = new SensorEventQueue(connection, *this, packageName);
break;
}
return queue;
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 0bcaadc..d31def7 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -20,9 +20,10 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
#include <sensor/BitTube.h>
@@ -67,7 +68,7 @@
static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
explicit SensorEventQueue(const sp<ISensorEventConnection>& connection,
- SensorManager& sensorManager);
+ SensorManager& sensorManager, String8 packageName);
virtual ~SensorEventQueue();
virtual void onFirstRef();
@@ -110,6 +111,7 @@
mutable sp<Looper> mLooper;
ASensorEvent* mRecBuffer;
SensorManager& mSensorManager;
+ String8 mPackageName;
size_t mAvailable;
size_t mConsumed;
uint32_t mNumAcksToSend;
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 42dd85e..23249fa 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -110,10 +110,12 @@
return mMatrix[i];
}
+// x translate
float Transform::tx() const {
return mMatrix[2][0];
}
+// y translate
float Transform::ty() const {
return mMatrix[2][1];
}
@@ -167,11 +169,15 @@
}
}
-void Transform::set(float a, float b, float c, float d) {
+// x and y are the coordinates in the destination (i.e. the screen)
+// s and t are the coordinates in the source (i.e. the texture)
+// d means derivative
+// dsdx means ds/dx derivative of s with respect to x, etc.
+void Transform::set(float dsdx, float dtdy, float dtdx, float dsdy) {
mat33& M(mMatrix);
- M[0][0] = a; M[1][0] = b;
- M[0][1] = c; M[1][1] = d;
- M[0][2] = 0; M[1][2] = 0;
+ M[0][0] = dsdx; M[1][0] = dtdy;
+ M[0][1] = dtdx; M[1][1] = dsdy;
+ M[0][2] = 0; M[1][2] = 0;
mType = UNKNOWN_TYPE;
}
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 2af51a7..d3b3a73 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -24,6 +24,10 @@
cc_defaults {
name: "libvibrator_defaults",
+ defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
+ ],
+
cflags: [
"-Wall",
"-Werror",
@@ -50,9 +54,11 @@
"libbinder",
"liblog",
"libutils",
+ "server_configurable_flags",
],
whole_static_libs: [
+ "android.os.vibrator.flags-aconfig-cc",
"libvibratorutils",
],
@@ -79,8 +85,14 @@
vendor_available: true,
double_loadable: true,
+ static_libs: [
+ "android.os.vibrator.flags-aconfig-cc",
+ ],
+
shared_libs: [
+ "liblog",
"libutils",
+ "server_configurable_flags",
],
srcs: [
@@ -89,6 +101,7 @@
visibility: [
"//frameworks/native/libs/vibrator",
+ "//frameworks/native/libs/vibrator/tests",
"//frameworks/av/media/libeffects/hapticgenerator",
],
}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index c97e496..cae2de2 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -93,8 +93,8 @@
externalVibrationScale.scaleLevel);
}
- return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/
- externalVibrationScale.adaptiveHapticsScale};
+ return os::HapticScale(scaleLevel, externalVibrationScale.scaleFactor,
+ externalVibrationScale.adaptiveHapticsScale);
}
} // namespace os
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 761ac1b..54afb71 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -13,10 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "ExternalVibrationUtils"
+
#include <cstring>
+#include <android_os_vibrator.h>
+
+#include <algorithm>
#include <math.h>
+#include <log/log.h>
#include <vibrator/ExternalVibrationUtils.h>
namespace android::os {
@@ -25,8 +31,10 @@
static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
+static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN
-float getHapticScaleGamma(HapticLevel level) {
+float getOldHapticScaleGamma(HapticLevel level) {
switch (level) {
case HapticLevel::VERY_LOW:
return 2.0f;
@@ -41,7 +49,7 @@
}
}
-float getHapticMaxAmplitudeRatio(HapticLevel level) {
+float getOldHapticMaxAmplitudeRatio(HapticLevel level) {
switch (level) {
case HapticLevel::VERY_LOW:
return HAPTIC_SCALE_VERY_LOW_RATIO;
@@ -56,6 +64,85 @@
}
}
+/* Same as VibrationScaler.getScaleFactor */
+float getHapticScaleFactor(HapticScale scale) {
+ if (android_os_vibrator_haptics_scale_v2_enabled()) {
+ if (scale.getScaleFactor() >= 0) {
+ // ExternalVibratorService provided the scale factor, use it.
+ return scale.getScaleFactor();
+ }
+
+ HapticLevel level = scale.getLevel();
+ switch (level) {
+ case HapticLevel::MUTE:
+ return 0.0f;
+ case HapticLevel::NONE:
+ return 1.0f;
+ default:
+ float scaleFactor = powf(SCALE_LEVEL_GAIN, static_cast<int32_t>(level));
+ if (scaleFactor <= 0) {
+ ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0",
+ scaleFactor, static_cast<int32_t>(level));
+ scaleFactor = 1.0f;
+ }
+ return scaleFactor;
+ }
+ }
+ // Same as VibrationScaler.SCALE_FACTOR_*
+ switch (scale.getLevel()) {
+ case HapticLevel::MUTE:
+ return 0.0f;
+ case HapticLevel::VERY_LOW:
+ return 0.6f;
+ case HapticLevel::LOW:
+ return 0.8f;
+ case HapticLevel::HIGH:
+ return 1.2f;
+ case HapticLevel::VERY_HIGH:
+ return 1.4f;
+ default:
+ return 1.0f;
+ }
+}
+
+float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) {
+ float sign = value >= 0 ? 1.0 : -1.0;
+ return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+ * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+}
+
+float applyNewHapticScale(float value, float scaleFactor) {
+ if (android_os_vibrator_haptics_scale_v2_enabled()) {
+ if (scaleFactor <= 1 || value == 0) {
+ return value * scaleFactor;
+ } else {
+ // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
+ return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value);
+ }
+ }
+ float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
+ if (scaleFactor <= 1) {
+ // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
+ // Scale up requires a different curve to ensure the intensity will not become > 1.
+ return value * scale;
+ }
+
+ float sign = value >= 0 ? 1.0f : -1.0f;
+ float extraScale = powf(scaleFactor, 4.0f - scaleFactor);
+ float x = fabsf(value) * scale * extraScale;
+ float maxX = scale * extraScale; // scaled x for intensity == 1
+
+ float expX = expf(x);
+ float expMaxX = expf(maxX);
+
+ // Using f = tanh as the scale up function so the max value will converge.
+ // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
+ float a = (expMaxX + 1.0f) / (expMaxX - 1.0f);
+ float fx = (expX - 1.0f) / (expX + 1.0f);
+
+ return sign * std::clamp(a * fx, 0.0f, 1.0f);
+}
+
void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
if (scale.isScaleMute()) {
memset(buffer, 0, length * sizeof(float));
@@ -65,15 +152,19 @@
return;
}
HapticLevel hapticLevel = scale.getLevel();
+ float scaleFactor = getHapticScaleFactor(scale);
float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
- float gamma = getHapticScaleGamma(hapticLevel);
- float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel);
+ float oldGamma = getOldHapticScaleGamma(hapticLevel);
+ float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
for (size_t i = 0; i < length; i++) {
if (hapticLevel != HapticLevel::NONE) {
- float sign = buffer[i] >= 0 ? 1.0 : -1.0;
- buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
- * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+ if (android_os_vibrator_fix_audio_coupled_haptics_scaling() ||
+ android_os_vibrator_haptics_scale_v2_enabled()) {
+ buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
+ } else {
+ buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
+ }
}
if (adaptiveScaleFactor != 1.0f) {
diff --git a/libs/vibrator/TEST_MAPPING b/libs/vibrator/TEST_MAPPING
new file mode 100644
index 0000000..d782b43
--- /dev/null
+++ b/libs/vibrator/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "libvibrator_test"
+ }
+ ]
+}
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index d9a2b81..322a2ac 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -17,9 +17,13 @@
#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+#include <cstring>
+#include <sstream>
+#include <string>
+
namespace android::os {
-enum class HapticLevel {
+enum class HapticLevel : int32_t {
MUTE = -100,
VERY_LOW = -2,
LOW = -1,
@@ -31,32 +35,42 @@
class HapticScale {
private:
HapticLevel mLevel = HapticLevel::NONE;
+float mScaleFactor = -1.0f; // undefined, use haptic level to define scale factor
float mAdaptiveScaleFactor = 1.0f;
public:
-constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor)
- : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
-constexpr HapticScale(HapticLevel level) : mLevel(level) {}
-constexpr HapticScale() {}
+ explicit HapticScale(HapticLevel level, float scaleFactor, float adaptiveScaleFactor)
+ : mLevel(level), mScaleFactor(scaleFactor), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
+ explicit HapticScale(HapticLevel level) : mLevel(level) {}
+ constexpr HapticScale() {}
-HapticLevel getLevel() const { return mLevel; }
-float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
+ HapticLevel getLevel() const { return mLevel; }
+ float getScaleFactor() const { return mScaleFactor; }
+ float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
-bool operator==(const HapticScale& other) const {
- return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
-}
+ bool operator==(const HapticScale& other) const {
+ return mLevel == other.mLevel && mScaleFactor == other.mScaleFactor &&
+ mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
+ }
bool isScaleNone() const {
- return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f;
+ return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f;
}
-bool isScaleMute() const {
- return mLevel == HapticLevel::MUTE;
+bool isScaleMute() const { return mLevel == HapticLevel::MUTE || mScaleFactor == 0; }
+
+std::string toString() const {
+ std::ostringstream os;
+ os << "HapticScale { level: " << static_cast<int>(mLevel);
+ os << ", scaleFactor: " << mScaleFactor;
+ os << ", adaptiveScaleFactor: " << mAdaptiveScaleFactor;
+ os << "}";
+ return os.str();
}
-static HapticScale mute() {
- return {/*level=*/os::HapticLevel::MUTE};
-}
+static HapticScale mute() { return os::HapticScale(os::HapticLevel::MUTE); }
+
+static HapticScale none() { return os::HapticScale(os::HapticLevel::NONE); }
};
bool isValidHapticScale(HapticScale scale);
diff --git a/libs/vibrator/tests/Android.bp b/libs/vibrator/tests/Android.bp
new file mode 100644
index 0000000..2921a62
--- /dev/null
+++ b/libs/vibrator/tests/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+ default_team: "trendy_team_haptics_framework",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "libvibrator_test",
+ test_suites: ["general-tests"],
+ defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
+ ],
+ srcs: [
+ "ExternalVibrationTest.cpp",
+ "ExternalVibrationUtilsTest.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ static_libs: [
+ "android.os.vibrator.flags-aconfig-cc",
+ "libflagtest",
+ "libgtest",
+ "liblog",
+ "libvibrator",
+ "libvibratorutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ "server_configurable_flags",
+ ],
+}
diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp
new file mode 100644
index 0000000..4133836
--- /dev/null
+++ b/libs/vibrator/tests/ExternalVibrationTest.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+#include <gtest/gtest.h>
+#include <vibrator/ExternalVibration.h>
+
+using namespace android;
+using namespace testing;
+
+using HapticLevel = os::HapticLevel;
+using ScaleLevel = os::ExternalVibrationScale::ScaleLevel;
+
+class TestVibrationController : public os::IExternalVibrationController {
+public:
+ explicit TestVibrationController() {}
+ IBinder *onAsBinder() override { return nullptr; }
+ binder::Status mute(/*out*/ bool *ret) override {
+ *ret = false;
+ return binder::Status::ok();
+ };
+ binder::Status unmute(/*out*/ bool *ret) override {
+ *ret = false;
+ return binder::Status::ok();
+ };
+};
+
+class ExternalVibrationTest : public Test {
+protected:
+ HapticLevel toHapticLevel(ScaleLevel level) {
+ os::ExternalVibrationScale externalVibrationScale;
+ externalVibrationScale.scaleLevel = level;
+ os::HapticScale hapticScale =
+ os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
+ return hapticScale.getLevel();
+ }
+};
+
+TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) {
+ int32_t uid = 1;
+ std::string pkg("package.name");
+ audio_attributes_t originalAttrs;
+ originalAttrs.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+ originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
+ originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE;
+
+ sp<TestVibrationController> vibrationController = new TestVibrationController();
+ ASSERT_NE(vibrationController, nullptr);
+
+ sp<os::ExternalVibration> original =
+ new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController);
+ ASSERT_NE(original, nullptr);
+
+ EXPECT_EQ(original->getUid(), uid);
+ EXPECT_EQ(original->getPackage(), pkg);
+ EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type);
+ EXPECT_EQ(original->getAudioAttributes().usage, originalAttrs.usage);
+ EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source);
+ EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags);
+ EXPECT_EQ(original->getController(), vibrationController);
+
+ audio_attributes_t defaultAttrs;
+ defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
+ defaultAttrs.usage = AUDIO_USAGE_UNKNOWN;
+ defaultAttrs.source = AUDIO_SOURCE_DEFAULT;
+ defaultAttrs.flags = AUDIO_FLAG_NONE;
+
+ sp<os::ExternalVibration> parceled =
+ new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr);
+ ASSERT_NE(parceled, nullptr);
+
+ Parcel parcel;
+ original->writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ parceled->readFromParcel(&parcel);
+
+ EXPECT_EQ(parceled->getUid(), uid);
+ EXPECT_EQ(parceled->getPackage(), pkg);
+ EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type);
+ EXPECT_EQ(parceled->getAudioAttributes().usage, originalAttrs.usage);
+ EXPECT_EQ(parceled->getAudioAttributes().source, originalAttrs.source);
+ EXPECT_EQ(parceled->getAudioAttributes().flags, originalAttrs.flags);
+ // TestVibrationController does not implement onAsBinder, skip controller parcel in this test.
+}
+
+TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) {
+ os::ExternalVibrationScale externalVibrationScale;
+ externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH;
+ externalVibrationScale.scaleFactor = 0.5f;
+ externalVibrationScale.adaptiveHapticsScale = 0.8f;
+
+ os::HapticScale hapticScale =
+ os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
+
+ // Check scale factors are forwarded.
+ EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH);
+ EXPECT_EQ(hapticScale.getScaleFactor(), 0.5f);
+ EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f);
+
+ // Check conversion for all levels.
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_LOW), HapticLevel::LOW);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_NONE), HapticLevel::NONE);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_HIGH), HapticLevel::HIGH);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_HIGH), HapticLevel::VERY_HIGH);
+}
diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
new file mode 100644
index 0000000..7adc9c3
--- /dev/null
+++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android_os_vibrator.h>
+#include <flag_macros.h>
+#include <gtest/gtest.h>
+#include <vibrator/ExternalVibrationUtils.h>
+
+#include "test_utils.h"
+
+#define FLAG_NS android::os::vibrator
+
+using namespace android;
+using namespace testing;
+
+using HapticScale = os::HapticScale;
+using HapticLevel = os::HapticLevel;
+
+static constexpr float TEST_TOLERANCE = 1e-2f;
+static constexpr size_t TEST_BUFFER_LENGTH = 4;
+static float TEST_BUFFER[TEST_BUFFER_LENGTH] = { 1, -1, 0.5f, -0.2f };
+
+class ExternalVibrationUtilsTest : public Test {
+public:
+ void SetUp() override {
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer));
+ }
+
+protected:
+ void scaleBuffer(HapticLevel hapticLevel) {
+ scaleBuffer(HapticScale(hapticLevel));
+ }
+
+ void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) {
+ scaleBuffer(hapticLevel, adaptiveScaleFactor, 0 /* limit */);
+ }
+
+ void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) {
+ scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit);
+ }
+
+ void scaleBuffer(HapticScale hapticScale) {
+ scaleBuffer(hapticScale, 0 /* limit */);
+ }
+
+ void scaleBuffer(HapticScale hapticScale, float limit) {
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer));
+ os::scaleHapticData(&mBuffer[0], TEST_BUFFER_LENGTH, hapticScale, limit);
+ }
+
+ float mBuffer[TEST_BUFFER_LENGTH];
+};
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleMute,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleMute,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2Mute,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleNone,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleNone,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2None,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleToHapticLevel,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.84f, -0.66f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.7f, -0.44f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.75f, -0.75f, 0.26f, -0.06f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.66f, -0.66f, 0.16f, -0.02f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleToHapticLevel,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.79f, -0.39f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.62f, -0.27f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.70f, -0.70f, 0.35f, -0.14f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.22f, -0.09f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToHapticLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.8f, -0.38f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.63f, -0.27f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.71f, -0.71f, 0.35f, -0.14f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.51f, -0.51f, 0.25f, -0.1f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorIgnoresLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ constexpr float adaptiveScaleNone = 1.0f;
+
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 1, -0.55f };
+ scaleBuffer(HapticScale(HapticLevel::LOW, 3.0f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.66f, -0.29f };
+ scaleBuffer(HapticScale(HapticLevel::LOW, 1.5f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.8f, -0.8f, 0.4f, -0.16f };
+ scaleBuffer(HapticScale(HapticLevel::HIGH, 0.8f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.4f, -0.4f, 0.2f, -0.08f };
+ scaleBuffer(HapticScale(HapticLevel::HIGH, 0.4f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.13f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 1.06f, -0.67f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.15f, -0.04f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.33f, -1.33f, 0.33f, -0.05f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.93f, -0.41f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.91f, -0.91f, 0.45f, -0.18f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.95f, -0.41f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.02f, -1.02f, 0.51f, -0.2f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.16f, -0.13f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.13f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1, -1, 0.5f, -0.2f };
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.33f, -0.05f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.16f, -0.13f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1, -1, 0.5f, -0.2f };
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.45f, -0.18f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestLimitAppliedAfterScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.15f, -0.07f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1.02f, -1.02f, 0.51f, -0.2f }
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.51f, -0.2f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
diff --git a/libs/vibrator/tests/test_utils.h b/libs/vibrator/tests/test_utils.h
new file mode 100644
index 0000000..f491ea1
--- /dev/null
+++ b/libs/vibrator/tests/test_utils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBVIBRATOR_TEST_UTILS_H
+#define LIBVIBRATOR_TEST_UTILS_H
+
+#include <gtest/gtest.h>
+
+#if !defined(EXPECT_FLOATS_NEARLY_EQ)
+#define EXPECT_FLOATS_NEARLY_EQ(expected, actual, length, epsilon) \
+ for (size_t i = 0; i < length; i++) { \
+ EXPECT_NEAR(expected[i], actual[i], epsilon) << " at Index: " << i; \
+ }
+#else
+#error Macro EXPECT_FLOATS_NEARLY_EQ already defined
+#endif
+
+#endif //LIBVIBRATOR_TEST_UTILS_H
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index c9e8b7c..782b6d9 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -6,39 +6,24 @@
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
-** This header is generated from the Khronos OpenGL / OpenGL ES XML
-** API Registry. The current version of the Registry, generator scripts
+** This header is generated from the Khronos EGL XML API Registry.
+** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
+** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $
*/
#include <EGL/eglplatform.h>
-/* Generated on date 20180517 */
+#ifndef EGL_EGL_PROTOTYPES
+#define EGL_EGL_PROTOTYPES 1
+#endif
+
+/* Generated on date 20240715 */
/* Generated C header for:
* API: egl
@@ -118,6 +103,31 @@
#define EGL_VERSION 0x3054
#define EGL_WIDTH 0x3057
#define EGL_WINDOW_BIT 0x0004
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCHOOSECONFIGPROC) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCOPYBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
+typedef EGLContext (EGLAPIENTRYP PFNEGLCREATECONTEXTPROC) (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERSURFACEPROC) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGATTRIBPROC) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGSPROC) (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETCURRENTDISPLAYPROC) (void);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLGETCURRENTSURFACEPROC) (EGLint readdraw);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETDISPLAYPROC) (EGLNativeDisplayType display_id);
+typedef EGLint (EGLAPIENTRYP PFNEGLGETERRORPROC) (void);
+typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC) (const char *procname);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLINITIALIZEPROC) (EGLDisplay dpy, EGLint *major, EGLint *minor);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLMAKECURRENTPROC) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLTERMINATEPROC) (EGLDisplay dpy);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITGLPROC) (void);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITNATIVEPROC) (EGLint engine);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
EGLAPI EGLContext EGLAPIENTRY eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
@@ -142,6 +152,7 @@
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate (EGLDisplay dpy);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL (void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative (EGLint engine);
+#endif
#endif /* EGL_VERSION_1_0 */
#ifndef EGL_VERSION_1_1
@@ -160,10 +171,16 @@
#define EGL_TEXTURE_RGB 0x305D
#define EGL_TEXTURE_RGBA 0x305E
#define EGL_TEXTURE_TARGET 0x3081
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDTEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSURFACEATTRIBPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPINTERVALPROC) (EGLDisplay dpy, EGLint interval);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval (EGLDisplay dpy, EGLint interval);
+#endif
#endif /* EGL_VERSION_1_1 */
#ifndef EGL_VERSION_1_2
@@ -199,11 +216,18 @@
#define EGL_SWAP_BEHAVIOR 0x3093
#define EGL_UNKNOWN EGL_CAST(EGLint,-1)
#define EGL_VERTICAL_RESOLUTION 0x3091
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDAPIPROC) (EGLenum api);
+typedef EGLenum (EGLAPIENTRYP PFNEGLQUERYAPIPROC) (void);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC) (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETHREADPROC) (void);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITCLIENTPROC) (void);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI (EGLenum api);
EGLAPI EGLenum EGLAPIENTRY eglQueryAPI (void);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread (void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void);
+#endif
#endif /* EGL_VERSION_1_2 */
#ifndef EGL_VERSION_1_3
@@ -232,7 +256,10 @@
#define EGL_OPENGL_API 0x30A2
#define EGL_OPENGL_BIT 0x0008
#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400
+typedef EGLContext (EGLAPIENTRYP PFNEGLGETCURRENTCONTEXTPROC) (void);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
+#endif
#endif /* EGL_VERSION_1_4 */
#ifndef EGL_VERSION_1_5
@@ -284,6 +311,17 @@
#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
#define EGL_IMAGE_PRESERVED 0x30D2
#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)
+typedef EGLSync (EGLAPIENTRYP PFNEGLCREATESYNCPROC) (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCPROC) (EGLDisplay dpy, EGLSync sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBPROC) (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
@@ -294,6 +332,7 @@
EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif
#endif /* EGL_VERSION_1_5 */
#ifdef __cplusplus
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index c787fc9..4d14c69 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -6,39 +6,20 @@
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
** This header is generated from the Khronos EGL XML API Registry.
** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: 726475c203 $ on $Git commit date: 2018-10-03 23:51:49 -0700 $
+** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $
*/
#include <EGL/eglplatform.h>
-#define EGL_EGLEXT_VERSION 20181204
+#define EGL_EGLEXT_VERSION 20240715
/* Generated C header for:
* API: egl
@@ -443,9 +424,9 @@
#ifndef EGL_KHR_swap_buffers_with_damage
#define EGL_KHR_swap_buffers_with_damage 1
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#endif
#endif /* EGL_KHR_swap_buffers_with_damage */
@@ -462,6 +443,10 @@
#endif
#endif /* EGL_KHR_wait_sync */
+#ifndef EGL_ANDROID_GLES_layers
+#define EGL_ANDROID_GLES_layers 1
+#endif /* EGL_ANDROID_GLES_layers */
+
#ifndef EGL_ANDROID_blob_cache
#define EGL_ANDROID_blob_cache 1
typedef khronos_ssize_t EGLsizeiANDROID;
@@ -566,6 +551,11 @@
#define EGL_RECORDABLE_ANDROID 0x3142
#endif /* EGL_ANDROID_recordable */
+#ifndef EGL_ANDROID_telemetry_hint
+#define EGL_ANDROID_telemetry_hint 1
+#define EGL_TELEMETRY_HINT_ANDROID 0x3570
+#endif /* EGL_ANDROID_telemetry_hint */
+
#ifndef EGL_ANGLE_d3d_share_handle_client_buffer
#define EGL_ANGLE_d3d_share_handle_client_buffer 1
#define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
@@ -589,11 +579,25 @@
#define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1
#endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */
+#ifndef EGL_ANGLE_sync_control_rate
+#define EGL_ANGLE_sync_control_rate 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETMSCRATEANGLEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateANGLE (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator);
+#endif
+#endif /* EGL_ANGLE_sync_control_rate */
+
#ifndef EGL_ANGLE_window_fixed_size
#define EGL_ANGLE_window_fixed_size 1
#define EGL_FIXED_SIZE_ANGLE 0x3201
#endif /* EGL_ANGLE_window_fixed_size */
+#ifndef EGL_ARM_image_format
+#define EGL_ARM_image_format 1
+#define EGL_COLOR_COMPONENT_TYPE_UNSIGNED_INTEGER_ARM 0x3287
+#define EGL_COLOR_COMPONENT_TYPE_INTEGER_ARM 0x3288
+#endif /* EGL_ARM_image_format */
+
#ifndef EGL_ARM_implicit_external_sync
#define EGL_ARM_implicit_external_sync 1
#define EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM 0x328A
@@ -652,6 +656,11 @@
#endif
#endif /* EGL_EXT_compositor */
+#ifndef EGL_EXT_config_select_group
+#define EGL_EXT_config_select_group 1
+#define EGL_CONFIG_SELECT_GROUP_EXT 0x34C0
+#endif /* EGL_EXT_config_select_group */
+
#ifndef EGL_EXT_create_context_robustness
#define EGL_EXT_create_context_robustness 1
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
@@ -684,6 +693,11 @@
#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif /* EGL_EXT_device_drm */
+#ifndef EGL_EXT_device_drm_render_node
+#define EGL_EXT_device_drm_render_node 1
+#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
+#endif /* EGL_EXT_device_drm_render_node */
+
#ifndef EGL_EXT_device_enumeration
#define EGL_EXT_device_enumeration 1
#endif /* EGL_EXT_device_enumeration */
@@ -691,12 +705,33 @@
#ifndef EGL_EXT_device_openwf
#define EGL_EXT_device_openwf 1
#define EGL_OPENWF_DEVICE_ID_EXT 0x3237
+#define EGL_OPENWF_DEVICE_EXT 0x333D
#endif /* EGL_EXT_device_openwf */
+#ifndef EGL_EXT_device_persistent_id
+#define EGL_EXT_device_persistent_id 1
+#define EGL_DEVICE_UUID_EXT 0x335C
+#define EGL_DRIVER_UUID_EXT 0x335D
+#define EGL_DRIVER_NAME_EXT 0x335E
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICEBINARYEXTPROC) (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDeviceBinaryEXT (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size);
+#endif
+#endif /* EGL_EXT_device_persistent_id */
+
#ifndef EGL_EXT_device_query
#define EGL_EXT_device_query 1
#endif /* EGL_EXT_device_query */
+#ifndef EGL_EXT_device_query_name
+#define EGL_EXT_device_query_name 1
+#define EGL_RENDERER_EXT 0x335F
+#endif /* EGL_EXT_device_query_name */
+
+#ifndef EGL_EXT_explicit_device
+#define EGL_EXT_explicit_device 1
+#endif /* EGL_EXT_explicit_device */
+
#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
#define EGL_EXT_gl_colorspace_bt2020_hlg 1
#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x3540
@@ -878,6 +913,17 @@
#define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6
#endif /* EGL_EXT_platform_x11 */
+#ifndef EGL_EXT_platform_xcb
+#define EGL_EXT_platform_xcb 1
+#define EGL_PLATFORM_XCB_EXT 0x31DC
+#define EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE
+#endif /* EGL_EXT_platform_xcb */
+
+#ifndef EGL_EXT_present_opaque
+#define EGL_EXT_present_opaque 1
+#define EGL_PRESENT_OPAQUE_EXT 0x31DF
+#endif /* EGL_EXT_present_opaque */
+
#ifndef EGL_EXT_protected_content
#define EGL_EXT_protected_content 1
#define EGL_PROTECTED_CONTENT_EXT 0x32C0
@@ -887,6 +933,10 @@
#define EGL_EXT_protected_surface 1
#endif /* EGL_EXT_protected_surface */
+#ifndef EGL_EXT_query_reset_notification_strategy
+#define EGL_EXT_query_reset_notification_strategy 1
+#endif /* EGL_EXT_query_reset_notification_strategy */
+
#ifndef EGL_EXT_stream_consumer_egloutput
#define EGL_EXT_stream_consumer_egloutput 1
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer);
@@ -916,11 +966,36 @@
#define EGL_METADATA_SCALING_EXT 50000
#endif /* EGL_EXT_surface_SMPTE2086_metadata */
+#ifndef EGL_EXT_surface_compression
+#define EGL_EXT_surface_compression 1
+#define EGL_SURFACE_COMPRESSION_EXT 0x34B0
+#define EGL_SURFACE_COMPRESSION_PLANE1_EXT 0x328E
+#define EGL_SURFACE_COMPRESSION_PLANE2_EXT 0x328F
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT 0x34B1
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT 0x34B2
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT 0x34B4
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_2BPC_EXT 0x34B5
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_3BPC_EXT 0x34B6
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_4BPC_EXT 0x34B7
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_5BPC_EXT 0x34B8
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_6BPC_EXT 0x34B9
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_7BPC_EXT 0x34BA
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_8BPC_EXT 0x34BB
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_9BPC_EXT 0x34BC
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_10BPC_EXT 0x34BD
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_11BPC_EXT 0x34BE
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT 0x34BF
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSUPPORTEDCOMPRESSIONRATESEXTPROC) (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQuerySupportedCompressionRatesEXT (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates);
+#endif
+#endif /* EGL_EXT_surface_compression */
+
#ifndef EGL_EXT_swap_buffers_with_damage
#define EGL_EXT_swap_buffers_with_damage 1
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#endif
#endif /* EGL_EXT_swap_buffers_with_damage */
@@ -1036,6 +1111,16 @@
#define EGL_PLATFORM_SURFACELESS_MESA 0x31DD
#endif /* EGL_MESA_platform_surfaceless */
+#ifndef EGL_MESA_query_driver
+#define EGL_MESA_query_driver 1
+typedef char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERCONFIGPROC) (EGLDisplay dpy);
+typedef const char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERNAMEPROC) (EGLDisplay dpy);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI char *EGLAPIENTRY eglGetDisplayDriverConfig (EGLDisplay dpy);
+EGLAPI const char *EGLAPIENTRY eglGetDisplayDriverName (EGLDisplay dpy);
+#endif
+#endif /* EGL_MESA_query_driver */
+
#ifndef EGL_NOK_swap_region
#define EGL_NOK_swap_region 1
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGIONNOKPROC) (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
@@ -1124,11 +1209,39 @@
#endif
#endif /* EGL_NV_post_sub_buffer */
+#ifndef EGL_NV_quadruple_buffer
+#define EGL_NV_quadruple_buffer 1
+#define EGL_QUADRUPLE_BUFFER_NV 0x3231
+#endif /* EGL_NV_quadruple_buffer */
+
#ifndef EGL_NV_robustness_video_memory_purge
#define EGL_NV_robustness_video_memory_purge 1
#define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C
#endif /* EGL_NV_robustness_video_memory_purge */
+#ifndef EGL_NV_stream_consumer_eglimage
+#define EGL_NV_stream_consumer_eglimage 1
+#define EGL_STREAM_CONSUMER_IMAGE_NV 0x3373
+#define EGL_STREAM_IMAGE_ADD_NV 0x3374
+#define EGL_STREAM_IMAGE_REMOVE_NV 0x3375
+#define EGL_STREAM_IMAGE_AVAILABLE_NV 0x3376
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMIMAGECONSUMERCONNECTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list);
+typedef EGLint (EGLAPIENTRYP PFNEGLQUERYSTREAMCONSUMEREVENTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMACQUIREIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMRELEASEIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamImageConsumerConnectNV (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list);
+EGLAPI EGLint EGLAPIENTRY eglQueryStreamConsumerEventNV (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamAcquireImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamReleaseImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync);
+#endif
+#endif /* EGL_NV_stream_consumer_eglimage */
+
+#ifndef EGL_NV_stream_consumer_eglimage_use_scanout_attrib
+#define EGL_NV_stream_consumer_eglimage_use_scanout_attrib 1
+#define EGL_STREAM_CONSUMER_IMAGE_USE_SCANOUT_NV 0x3378
+#endif /* EGL_NV_stream_consumer_eglimage_use_scanout_attrib */
+
#ifndef EGL_NV_stream_consumer_gltexture_yuv
#define EGL_NV_stream_consumer_gltexture_yuv 1
#define EGL_YUV_PLANE0_TEXTURE_UNIT_NV 0x332C
@@ -1165,6 +1278,12 @@
#define EGL_STREAM_CROSS_SYSTEM_NV 0x334F
#endif /* EGL_NV_stream_cross_system */
+#ifndef EGL_NV_stream_dma
+#define EGL_NV_stream_dma 1
+#define EGL_STREAM_DMA_NV 0x3371
+#define EGL_STREAM_DMA_SERVER_NV 0x3372
+#endif /* EGL_NV_stream_dma */
+
#ifndef EGL_NV_stream_fifo_next
#define EGL_NV_stream_fifo_next 1
#define EGL_PENDING_FRAME_NV 0x3329
@@ -1216,6 +1335,21 @@
#endif
#endif /* EGL_NV_stream_metadata */
+#ifndef EGL_NV_stream_origin
+#define EGL_NV_stream_origin 1
+#define EGL_STREAM_FRAME_ORIGIN_X_NV 0x3366
+#define EGL_STREAM_FRAME_ORIGIN_Y_NV 0x3367
+#define EGL_STREAM_FRAME_MAJOR_AXIS_NV 0x3368
+#define EGL_CONSUMER_AUTO_ORIENTATION_NV 0x3369
+#define EGL_PRODUCER_AUTO_ORIENTATION_NV 0x336A
+#define EGL_LEFT_NV 0x336B
+#define EGL_RIGHT_NV 0x336C
+#define EGL_TOP_NV 0x336D
+#define EGL_BOTTOM_NV 0x336E
+#define EGL_X_AXIS_NV 0x336F
+#define EGL_Y_AXIS_NV 0x3370
+#endif /* EGL_NV_stream_origin */
+
#ifndef EGL_NV_stream_remote
#define EGL_NV_stream_remote 1
#define EGL_STREAM_STATE_INITIALIZING_NV 0x3240
@@ -1312,6 +1446,21 @@
#endif /* KHRONOS_SUPPORT_INT64 */
#endif /* EGL_NV_system_time */
+#ifndef EGL_NV_triple_buffer
+#define EGL_NV_triple_buffer 1
+#define EGL_TRIPLE_BUFFER_NV 0x3230
+#endif /* EGL_NV_triple_buffer */
+
+#ifndef EGL_QNX_image_native_buffer
+#define EGL_QNX_image_native_buffer 1
+#define EGL_NATIVE_BUFFER_QNX 0x3551
+#endif /* EGL_QNX_image_native_buffer */
+
+#ifndef EGL_QNX_platform_screen
+#define EGL_QNX_platform_screen 1
+#define EGL_PLATFORM_SCREEN_QNX 0x3550
+#endif /* EGL_QNX_platform_screen */
+
#ifndef EGL_TIZEN_image_native_buffer
#define EGL_TIZEN_image_native_buffer 1
#define EGL_NATIVE_BUFFER_TIZEN 0x32A0
@@ -1322,6 +1471,40 @@
#define EGL_NATIVE_SURFACE_TIZEN 0x32A1
#endif /* EGL_TIZEN_image_native_surface */
+#ifndef EGL_WL_bind_wayland_display
+#define EGL_WL_bind_wayland_display 1
+#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC
+struct wl_display;
+struct wl_resource;
+#define EGL_WAYLAND_BUFFER_WL 0x31D5
+#define EGL_WAYLAND_PLANE_WL 0x31D6
+#define EGL_TEXTURE_Y_U_V_WL 0x31D7
+#define EGL_TEXTURE_Y_UV_WL 0x31D8
+#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWLPROC) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#endif
+#endif /* EGL_WL_bind_wayland_display */
+
+#ifndef EGL_WL_create_wayland_buffer_from_image
+#define EGL_WL_create_wayland_buffer_from_image 1
+#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC
+struct wl_buffer;
+typedef struct wl_buffer *(EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC) (EGLDisplay dpy, EGLImageKHR image);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI struct wl_buffer *EGLAPIENTRY eglCreateWaylandBufferFromImageWL (EGLDisplay dpy, EGLImageKHR image);
+#endif
+#endif /* EGL_WL_create_wayland_buffer_from_image */
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 0bc2cb9..6786afd 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -2,36 +2,17 @@
#define __eglplatform_h_
/*
-** Copyright (c) 2007-2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+** Copyright 2007-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
*/
/* Platform-specific types and definitions for egl.h
- * $Revision: 30994 $ on $Date: 2015-04-30 13:36:48 -0700 (Thu, 30 Apr 2015) $
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
* they can be included in future versions of this file. Please submit changes
- * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
- * by filing a bug against product "EGL" component "Registry".
+ * by filing an issue or pull request on the public Khronos EGL Registry, at
+ * https://www.github.com/KhronosGroup/EGL-Registry/
*/
#include <KHR/khrplatform.h>
@@ -67,7 +48,13 @@
* implementations.
*/
-#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES)
+
+typedef void *EGLNativeDisplayType;
+typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
+
+#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
@@ -77,11 +64,23 @@
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
+#elif defined(__QNX__)
+
+typedef khronos_uintptr_t EGLNativeDisplayType;
+typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */
+typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */
+
+#elif defined(__EMSCRIPTEN__)
+
+typedef int EGLNativeDisplayType;
+typedef int EGLNativePixmapType;
+typedef int EGLNativeWindowType;
+
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
-typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
#elif defined(WL_EGL_PLATFORM)
@@ -100,17 +99,17 @@
struct ANativeWindow;
struct egl_native_pixmap_t;
-typedef struct ANativeWindow* EGLNativeWindowType;
-typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
+typedef struct egl_native_pixmap_t* EGLNativePixmapType;
+typedef struct ANativeWindow* EGLNativeWindowType;
#elif defined(USE_OZONE)
typedef intptr_t EGLNativeDisplayType;
-typedef intptr_t EGLNativeWindowType;
typedef intptr_t EGLNativePixmapType;
+typedef intptr_t EGLNativeWindowType;
-#elif defined(__unix__) || defined(USE_X11)
+#elif defined(USE_X11)
/* X11 (tentative) */
#include <X11/Xlib.h>
@@ -120,11 +119,17 @@
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
+#elif defined(__unix__)
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#elif defined(__APPLE__)
typedef int EGLNativeDisplayType;
-typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
#elif defined(__HAIKU__)
@@ -134,6 +139,12 @@
typedef khronos_uintptr_t EGLNativePixmapType;
typedef khronos_uintptr_t EGLNativeWindowType;
+#elif defined(__Fuchsia__)
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#else
#error "Platform not recognized"
#endif
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index ec7b190..bf0e38e 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -270,7 +270,7 @@
hnd = attempt_to_load_updated_driver(cnx);
// If updated driver apk is set but fail to load, abort here.
- LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(),
+ LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace() && !hnd,
"couldn't find an OpenGL ES implementation from %s",
android::GraphicsEnv::getInstance().getDriverPath().c_str());
}
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 502c14f..8cb637b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -248,7 +248,7 @@
return cnx->platform.eglGetProcAddress(procname);
}
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, const EGLint* rects,
EGLint n_rects) {
ATRACE_CALL();
clearError();
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 1c91f1d..b6f2c34 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -106,5 +106,5 @@
/* Partial update extensions */
-EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint *, EGLint)
EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index a6af713..6e35041 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -916,42 +916,72 @@
egl_context_t* const c = get_context(share_list);
share_list = c->context;
}
+
+ bool skip_telemetry = false;
+
+ auto findAttribute = [](const EGLint* attrib_ptr, GLint attribute, GLint* value) {
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr == attribute) {
+ if (value) {
+ *value = val;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ std::vector<EGLint> replacement_attrib_list;
+ GLint telemetry_value;
+ if (findAttribute(attrib_list, EGL_TELEMETRY_HINT_ANDROID, &telemetry_value)) {
+ skip_telemetry = (telemetry_value == android::GpuStatsInfo::SKIP_TELEMETRY);
+
+ // We need to remove EGL_TELEMETRY_HINT_ANDROID or the underlying drivers will
+ // complain about an unexpected attribute
+ const EGLint* attrib_ptr = attrib_list;
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr != EGL_TELEMETRY_HINT_ANDROID) {
+ replacement_attrib_list.push_back(attr);
+ replacement_attrib_list.push_back(val);
+ }
+ }
+ replacement_attrib_list.push_back(EGL_NONE);
+ attrib_list = replacement_attrib_list.data();
+ }
// b/111083885 - If we are presenting EGL 1.4 interface to apps
// error out on robust access attributes that are invalid
// in EGL 1.4 as the driver may be fine with them but dEQP expects
// tests to fail according to spec.
if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
- const EGLint* attrib_ptr = attrib_list;
- while (*attrib_ptr != EGL_NONE) {
- GLint attr = *attrib_ptr++;
- GLint value = *attrib_ptr++;
- if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
- // We are GL ES context with EGL 1.4, this is an invalid
- // attribute
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
- }
- };
+ if (findAttribute(attrib_list, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
+ nullptr)) {
+ // We are GL ES context with EGL 1.4, this is an invalid attribute
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
}
EGLContext context =
cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- };
+ GLint version_value;
+ if (findAttribute(attrib_list, EGL_CONTEXT_CLIENT_VERSION, &version_value)) {
+ if (version_value == 2 || version_value == 3) {
+ version = egl_connection_t::GLESv2_INDEX;
+ }
}
if (version == egl_connection_t::GLESv1_INDEX) {
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::GLES_1_IN_USE);
}
- android::GraphicsEnv::getInstance().setTargetStats(
- android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ if (!skip_telemetry) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ }
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
return c;
}
@@ -1324,7 +1354,7 @@
std::mutex mMutex;
};
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, const EGLint* rects,
EGLint n_rects) {
const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
index 4a0d4b9..dc99e09 100644
--- a/opengl/libs/GLES2/gl2ext_api.in
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -427,9 +427,6 @@
void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex);
}
-void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
- CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex);
-}
void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) {
CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount);
}
diff --git a/opengl/libs/entries.in b/opengl/libs/entries.in
index a306510..4c1eefc 100644
--- a/opengl/libs/entries.in
+++ b/opengl/libs/entries.in
@@ -605,7 +605,6 @@
GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride)
GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
-GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount)
GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride)
GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
index 4673411..004aa5a 100644
--- a/opengl/libs/platform_entries.in
+++ b/opengl/libs/platform_entries.in
@@ -24,7 +24,7 @@
EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
EGL_ENTRY(EGLint, eglGetError, void)
EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
-EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint*, EGLint)
EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
index 680b291..a391c81 100644
--- a/services/gpuservice/bpfprogs/Android.bp
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -24,9 +24,4 @@
bpf {
name: "gpuMem.o",
srcs: ["gpuMem.c"],
- btf: true,
- cflags: [
- "-Wall",
- "-Werror",
- ],
}
diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp
index fe45c98..2e444fe 100644
--- a/services/gpuservice/gpuwork/bpfprogs/Android.bp
+++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp
@@ -20,11 +20,7 @@
name: "gpuWork.o",
srcs: ["gpuWork.c"],
cflags: [
- "-Wall",
- "-Werror",
- "-Wformat",
"-Wthread-safety",
- "-Wunused",
"-Wunreachable-code",
],
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 70801dc..cb220ab 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -172,9 +172,8 @@
export_static_lib_headers: [
"libinputdispatcher",
],
- export_include_dirs: [
- ".",
- "include",
+ export_shared_lib_headers: [
+ "libinputflinger_base",
],
}
@@ -185,7 +184,16 @@
cc_library_headers {
name: "libinputflinger_headers",
host_supported: true,
- export_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ ".",
+ ],
+ header_libs: [
+ "libchrome-gestures_headers",
+ ],
+ export_header_lib_headers: [
+ "libchrome-gestures_headers",
+ ],
}
filegroup {
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 82a93f7..af9d2eb 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -152,9 +152,6 @@
},
{
"name": "CtsSurfaceControlTests"
- },
- {
- "name": "WmTests"
}
],
"postsubmit": [
@@ -298,7 +295,7 @@
"name": "monkey_test"
},
{
- "name": "WmTests"
+ "name": "CtsInputRootTestCases"
}
],
"staged-platinum-postsubmit": [
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index af4a04d..0384257 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -693,7 +693,8 @@
*/
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
const TouchState& newTouchState,
- const MotionEntry& entry) {
+ const MotionEntry& entry,
+ std::function<void()> dump) {
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
@@ -741,6 +742,7 @@
// crashing the device with FATAL.
severity = android::base::LogSeverity::ERROR;
}
+ dump();
LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
}
touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
@@ -2683,7 +2685,7 @@
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry.deviceId, pointer, targets);
+ tempTouchState, entry, targets);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -2710,7 +2712,8 @@
// Update dispatching for hover enter and exit.
{
std::vector<TouchedWindow> hoveringWindows =
- getHoveringWindowsLocked(oldState, tempTouchState, entry);
+ getHoveringWindowsLocked(oldState, tempTouchState, entry,
+ [this]() REQUIRES(mLock) { logDispatchStateLocked(); });
// Hardcode to single hovering pointer for now.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(entry.pointerProperties[0].id);
@@ -4879,6 +4882,10 @@
logDispatchStateLocked();
LOG(ERROR) << "Inconsistent event: " << motionEvent
<< ", reason: " << result.error();
+ if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+ mLock.unlock();
+ return InputEventInjectionResult::FAILED;
+ }
}
}
@@ -7096,9 +7103,11 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const {
+ LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
+ const DeviceId deviceId = entry.deviceId;
+ const PointerProperties& pointerProperties = entry.pointerProperties[0];
std::vector<PointerProperties> pointers{pointerProperties};
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7125,7 +7134,7 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointers);
+ deviceId, pointers, entry.eventTime);
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 698bdba..87dfd1d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -709,11 +709,23 @@
sp<InputReporterInterface> mReporter;
+ /**
+ * Slip the wallpaper touch if necessary.
+ *
+ * @param targetFlags the target flags
+ * @param oldWindowHandle the old window that the touch slipped out of
+ * @param newWindowHandle the new window that the touch is slipping into
+ * @param state the current touch state. This will be updated if necessary to reflect the new
+ * windows that are receiving touch.
+ * @param deviceId the device id of the current motion being processed
+ * @param pointerProperties the pointer properties of the current motion being processed
+ * @param targets the current targets to add the walpaper ones to
+ * @param eventTime the new downTime for the wallpaper target
+ */
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9d4bb3d..5a70dd5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,7 @@
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
+ std::optional<nsecs_t> firstDownTimeInTarget);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index cb525a4..96e619c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -65,10 +65,10 @@
void onEventProcessingComplete(nsecs_t processingTimestamp);
InputTracer& tracer;
- std::vector<const TracedEvent> events;
+ std::vector<TracedEvent> events;
bool isEventProcessingComplete{false};
// A queue to hold dispatch args from being traced until event processing is complete.
- std::vector<const WindowDispatchArgs> pendingDispatchArgs;
+ std::vector<WindowDispatchArgs> pendingDispatchArgs;
// The metadata should not be modified after event processing is complete.
TracedEventMetadata metadata{};
};
diff --git a/services/inputflinger/include/TouchpadHardwareState.h b/services/inputflinger/include/TouchpadHardwareState.h
new file mode 100644
index 0000000..a8dd44c
--- /dev/null
+++ b/services/inputflinger/include/TouchpadHardwareState.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <include/gestures.h>
+#include <vector>
+
+namespace android {
+
+// A Gestures library HardwareState struct (from libchrome-gestures), but bundled
+// with a vector to contain its FingerStates, so you don't have to worry about where
+// that memory is allocated.
+struct SelfContainedHardwareState {
+ HardwareState state;
+ std::vector<FingerState> fingers;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 7cc8940..4cd37d7 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -33,7 +33,7 @@
void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- if (mRawPressureAxis) {
+ if (mRawPressureAxis || mTouchButtonAccumulator.hasButtonTouch()) {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f);
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 984e217..5c90cbb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -149,7 +149,10 @@
// The SOURCE_BLUETOOTH_STYLUS is added to events dynamically if the current stream is modified
// by the external stylus state. That's why we don't add it directly to mSource during
// configuration.
- return mSource | (hasExternalStylus() ? AINPUT_SOURCE_BLUETOOTH_STYLUS : 0);
+ return mSource |
+ (mExternalStylusPresence == ExternalStylusPresence::TOUCH_FUSION
+ ? AINPUT_SOURCE_BLUETOOTH_STYLUS
+ : 0);
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
@@ -270,8 +273,8 @@
}
dump += INDENT3 "Stylus Fusion:\n";
- dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
- toString(mExternalStylusConnected));
+ dump += StringPrintf(INDENT4 "ExternalStylusPresence: %s\n",
+ ftl::enum_string(mExternalStylusPresence).c_str());
dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n",
toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
@@ -356,11 +359,19 @@
void TouchInputMapper::resolveExternalStylusPresence() {
std::vector<InputDeviceInfo> devices;
getContext()->getExternalStylusDevices(devices);
- mExternalStylusConnected = !devices.empty();
-
- if (!mExternalStylusConnected) {
+ if (devices.empty()) {
+ mExternalStylusPresence = ExternalStylusPresence::NONE;
resetExternalStylus();
+ return;
}
+ mExternalStylusPresence =
+ std::any_of(devices.begin(), devices.end(),
+ [](const auto& info) {
+ return info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
+ AINPUT_SOURCE_STYLUS) != nullptr;
+ })
+ ? ExternalStylusPresence::TOUCH_FUSION
+ : ExternalStylusPresence::BUTTON_FUSION;
}
TouchInputMapper::Parameters TouchInputMapper::computeParameters(
@@ -520,7 +531,7 @@
}
bool TouchInputMapper::hasExternalStylus() const {
- return mExternalStylusConnected;
+ return mExternalStylusPresence != ExternalStylusPresence::NONE;
}
/**
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 87b72af..ef0e02f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -365,6 +365,16 @@
RawState mLastRawState;
CookedState mLastCookedState;
+ enum class ExternalStylusPresence {
+ // No external stylus connected.
+ NONE,
+ // An external stylus that can report touch/pressure that can be fused with the touchscreen.
+ TOUCH_FUSION,
+ // An external stylus that can only report buttons.
+ BUTTON_FUSION,
+ ftl_last = BUTTON_FUSION,
+ };
+ ExternalStylusPresence mExternalStylusPresence{ExternalStylusPresence::NONE};
// State provided by an external stylus
StylusState mExternalStylusState;
// If an external stylus is capable of reporting pointer-specific data like pressure, we will
@@ -460,8 +470,6 @@
float mTiltYCenter;
float mTiltYScale;
- bool mExternalStylusConnected;
-
// Oriented motion ranges for input device info.
struct OrientedRanges {
InputDeviceInfo::MotionRange x;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 07e62c6..66d62f8 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -26,18 +26,12 @@
#include "accumulator/CursorButtonAccumulator.h"
#include "accumulator/MultiTouchMotionAccumulator.h"
#include "accumulator/TouchButtonAccumulator.h"
+#include "include/TouchpadHardwareState.h"
#include "include/gestures.h"
namespace android {
-// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have
-// to worry about where that memory is allocated.
-struct SelfContainedHardwareState {
- HardwareState state;
- std::vector<FingerState> fingers;
-};
-
// Converts RawEvents into the HardwareState structs used by the gestures library.
class HardwareStateConverter {
public:
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e505850..c2f174f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5037,6 +5037,54 @@
}
/**
+ * Invalid events injected by input filter are rejected.
+ */
+TEST_F(InputDispatcherTest, InvalidA11yEventsGetRejected) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // a11y sets 'POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY' policy flag during injection, so define
+ // a custom injection function here for convenience.
+ auto injectFromAccessibility = [&](int32_t action, float x, float y) {
+ MotionEvent event = MotionEventBuilder(action, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(x).y(y))
+ .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT)
+ .build();
+ return injectMotionEvent(*mDispatcher, event, 100ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, /*targetUid=*/{},
+ POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_FILTERED |
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY);
+ };
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/300, /*y=*/400));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_MOVE, /*x=*/310, /*y=*/420));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ // finger is still down, so a new DOWN event should be rejected!
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/340, /*y=*/410));
+
+ // if the gesture is correctly finished, new down event will succeed
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_MOVE, /*x=*/320, /*y=*/430));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_UP, /*x=*/320, /*y=*/430));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/350, /*y=*/460));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
* If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
*/
TEST_F(InputDispatcherTest, TouchDownAfterMouseHover_legacy) {
@@ -5663,6 +5711,273 @@
}
/**
+ * Three windows:
+ * 1) A window on the left, with flag dup_to_wallpaper
+ * 2) A window on the right, with flag slippery
+ * 3) A wallpaper window under the left window
+ * When touch slips from right window to left, the wallpaper should receive a similar slippery
+ * enter event. Later on, when another device becomes active, the wallpaper should receive
+ * consistent streams from the new device, and also from the old device.
+ * This test attempts to reproduce a crash in the dispatcher where the wallpaper target's downTime
+ * was not getting set during slippery entrance.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ auto firstFingerMoveFromDeviceA = AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE),
+ WithPointerCount(1), WithPointerId(0, 0));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Same test as above, but with enable_multi_device_same_window_stream flag set to false.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ // This device was already canceled, so MOVE events will not be arriving to the windows from it.
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->assertNoEvents();
+}
+
+/**
* Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
* down event to the right window. Device B sends a down event to the left window, and then a
* POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 48fd717..4a9e893 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6675,15 +6675,27 @@
class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
public:
- SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
+ void SetUp() override {
+ SingleTouchInputMapperTest::SetUp();
+ mExternalStylusDeviceInfo = {};
+ mStylusState = {};
+ }
+
+ SingleTouchInputMapper& initializeInputMapperWithExternalStylus(bool supportsPressure = true) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
+ if (supportsPressure) {
+ mExternalStylusDeviceInfo.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
+ AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
+ mStylusState.pressure = 0.f;
+ }
+
mStylusState.when = ARBITRARY_TIME;
- mStylusState.pressure = 0.f;
mStylusState.toolType = ToolType::STYLUS;
mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
@@ -6791,11 +6803,17 @@
InputDeviceInfo mExternalStylusDeviceInfo{};
};
-TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
+TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSourceWithPressure) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
ASSERT_EQ(STYLUS_FUSION_SOURCE, mapper.getSources());
}
+TEST_F(ExternalStylusFusionTest, DoesNotUseBluetoothStylusSourceWithoutPressure) {
+ SingleTouchInputMapper& mapper =
+ initializeInputMapperWithExternalStylus(/*supportsPressure=*/false);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+}
+
TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 555b80a..33724a9 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -26,12 +26,17 @@
using util::ProtoOutputStream;
-SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
- uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId)
- : mService(service), mUid(uid), mMem(*mem),
+SensorService::SensorDirectConnection::SensorDirectConnection(
+ const sp<SensorService>& service, uid_t uid, pid_t pid, const sensors_direct_mem_t* mem,
+ int32_t halChannelHandle, const String16& opPackageName, int deviceId)
+ : mService(service),
+ mUid(uid),
+ mPid(pid),
+ mMem(*mem),
mHalChannelHandle(halChannelHandle),
- mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
+ mOpPackageName(opPackageName),
+ mDeviceId(deviceId),
+ mDestroyed(false) {
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -62,10 +67,21 @@
void SensorService::SensorDirectConnection::dump(String8& result) const {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
- String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size());
- for (auto &i : mActivated) {
- result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
+ result.appendFormat("\t%s | HAL channel handle %d | uid %d | pid %d\n",
+ String8(mOpPackageName).c_str(), getHalChannelHandle(), mUid, mPid);
+ result.appendFormat("\tActivated sensor count: %zu\n", mActivated.size());
+ dumpSensorInfoWithLock(result, mActivated);
+
+ result.appendFormat("\tBackup sensor (opened but UID idle) count: %zu\n",
+ mActivatedBackup.size());
+ dumpSensorInfoWithLock(result, mActivatedBackup);
+}
+
+void SensorService::SensorDirectConnection::dumpSensorInfoWithLock(
+ String8& result, std::unordered_map<int, int> sensors) const {
+ for (auto& i : sensors) {
+ result.appendFormat("\t\t%s 0x%08x | rate %d\n", mService->getSensorName(i.first).c_str(),
+ i.first, i.second);
}
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index bfaf811..9f21731 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
#define ANDROID_SENSOR_DIRECT_CONNECTION_H
-#include <optional>
+#include <android-base/thread_annotations.h>
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <binder/BinderService.h>
@@ -37,15 +38,15 @@
class SensorService::SensorDirectConnection: public BnSensorEventConnection {
public:
- SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
- const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId);
+ SensorDirectConnection(const sp<SensorService>& service, uid_t uid, pid_t pid,
+ const sensors_direct_mem_t* mem, int32_t halChannelHandle,
+ const String16& opPackageName, int deviceId);
void dump(String8& result) const;
void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
const String16& getOpPackageName() const { return mOpPackageName; }
int32_t getHalChannelHandle() const;
- bool isEquivalent(const sensors_direct_mem_t *mem) const;
+ bool isEquivalent(const sensors_direct_mem_t* mem) const;
// Invoked when access to sensors for this connection has changed, e.g. lost or
// regained due to changes in the sensor restricted/privacy mode or the
@@ -94,8 +95,13 @@
// Recover sensor requests previously capped by capRates().
void uncapRates();
+ // Dumps a set of sensor infos.
+ void dumpSensorInfoWithLock(String8& result, std::unordered_map<int, int> sensors) const
+ EXCLUSIVE_LOCKS_REQUIRED(mConnectionLock);
+
const sp<SensorService> mService;
const uid_t mUid;
+ const pid_t mPid;
const sensors_direct_mem_t mMem;
const int32_t mHalChannelHandle;
const String16 mOpPackageName;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 3446f58..130c112 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -90,15 +90,14 @@
result.append("NORMAL\n");
}
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
- "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize,
- mMaxCacheSize);
+ "max cache size %d | has sensor access: %s\n",
+ mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize,
+ hasSensorAccess() ? "true" : "false");
for (auto& it : mSensorInfo) {
const FlushInfo& flushInfo = it.second;
- result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
- mService->getSensorName(it.first).c_str(),
- it.first,
- flushInfo.mFirstFlushPending ? "First flush pending" :
- "active",
+ result.appendFormat("\t %s 0x%08x | first flush pending: %s | pending flush events %d \n",
+ mService->getSensorName(it.first).c_str(), it.first,
+ flushInfo.mFirstFlushPending ? "true" : "false",
flushInfo.mPendingFlushEventsToSend);
}
#if DEBUG_CONNECTIONS
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 31b7f88..3895ffe 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -682,14 +682,14 @@
mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
const auto& activeConnections = connLock.getActiveConnections();
- result.appendFormat("%zd active connections\n", activeConnections.size());
+ result.appendFormat("%zd open event connections\n", activeConnections.size());
for (size_t i=0 ; i < activeConnections.size() ; i++) {
result.appendFormat("Connection Number: %zu \n", i);
activeConnections[i]->dump(result);
}
const auto& directConnections = connLock.getDirectConnections();
- result.appendFormat("%zd direct connections\n", directConnections.size());
+ result.appendFormat("%zd open direct connections\n", directConnections.size());
for (size_t i = 0 ; i < directConnections.size() ; i++) {
result.appendFormat("Direct connection %zu:\n", i);
directConnections[i]->dump(result);
@@ -1729,7 +1729,10 @@
ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
} else {
mem.handle = clone;
- conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ conn = new SensorDirectConnection(this, uid, pid, &mem, channelHandle, opPackageName,
+ deviceId);
}
if (conn == nullptr) {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b826466..ed4c47d 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -48,6 +48,7 @@
"libtonemap",
"libaidlcommonsupport",
"libprocessgroup",
+ "libprocessgroup_util",
"libcgrouprc",
"libjsoncpp",
"libcgrouprc_format",
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c5008d8..7c4aa75 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -209,7 +209,7 @@
if (!buffer || buffer->initCheck() != ::android::OK) {
return nullptr;
}
- return std::move(buffer);
+ return buffer;
}
} // anonymous namespace
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
index dfb1c1e..2088635 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -114,7 +114,7 @@
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setLayer(mSurfaceControl->get(), INT32_MAX - 2)
.setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
@@ -130,7 +130,7 @@
}
void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
- SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+ createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
}
void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
@@ -141,7 +141,7 @@
// set the ratio frame to the top right of the screen
frame.offsetBy(viewport.width - frame.width(), height >> 4);
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
.setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -167,7 +167,7 @@
}
}();
- SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+ createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
constexpr SkColor kMinRatioColor = SK_ColorBLUE;
constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
@@ -194,9 +194,21 @@
void HdrSdrRatioOverlay::animate() {
if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
.apply();
}
+SurfaceComposerClient::Transaction HdrSdrRatioOverlay::createTransaction() const {
+ constexpr float kFrameRate = 0.f;
+ constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+ const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+ SurfaceComposerClient::Transaction transaction;
+ transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+ return transaction;
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
index 72d401d..ba88252 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.h
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -53,5 +53,7 @@
size_t mIndex = 0;
std::array<sp<GraphicBuffer>, 2> mRingBuffer;
+
+ SurfaceComposerClient::Transaction createTransaction() const;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index f430526..2b9e88c 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -28,7 +28,6 @@
virtual void requestHardwareVsync(PhysicalDisplayId, bool enabled) = 0;
virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
- virtual void triggerOnFrameRateOverridesChanged() = 0;
virtual void onChoreographerAttached() = 0;
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 998b1b8..a398c01 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -210,6 +210,8 @@
// within the timeout of DisplayPowerTimer.
bool powerOnImminent = false;
+ bool shouldEmitEvent() const { return !idle; }
+
bool operator==(GlobalSignals other) const {
return touch == other.touch && idle == other.idle &&
powerOnImminent == other.powerOnImminent;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 799bc6d..80d5499 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -403,14 +403,20 @@
eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
}
-void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) {
- const bool supportsFrameRateOverrideByContent =
- pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
+void Scheduler::onFrameRateOverridesChanged() {
+ const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] {
+ std::scoped_lock lock(mDisplayLock);
+ const auto pacesetterOpt = pacesetterDisplayLocked();
+ LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+ const Display& pacesetter = *pacesetterOpt;
+ return std::make_pair(FTL_FAKE_GUARD(kMainThreadContext, *mPacesetterDisplayId),
+ pacesetter.selectorPtr->supportsAppFrameRateOverrideByContent());
+ }();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
- eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides));
+ eventThreadFor(Cycle::Render).onFrameRateOverridesChanged(pacesetterId, std::move(overrides));
}
void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId,
@@ -418,50 +424,49 @@
eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
}
-void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
- {
+bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
+ const bool isPacesetter =
+ FTL_FAKE_GUARD(kMainThreadContext,
+ (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
+
+ if (isPacesetter) {
std::lock_guard<std::mutex> lock(mPolicyLock);
- // Cache the last reported modes for primary display.
- mPolicy.cachedModeChangedParams = {cycle, mode};
+ mPolicy.emittedModeOpt = mode;
// Invalidate content based refresh rate selection so it could be calculated
// again for the new refresh rate.
mPolicy.contentRequirements.clear();
}
- onNonPrimaryDisplayModeChanged(cycle, mode);
-}
-void Scheduler::dispatchCachedReportedMode() {
- // Check optional fields first.
- if (!mPolicy.modeOpt) {
- ALOGW("No mode ID found, not dispatching cached mode.");
- return;
- }
- if (!mPolicy.cachedModeChangedParams) {
- ALOGW("No mode changed params found, not dispatching cached mode.");
- return;
- }
-
- // If the mode is not the current mode, this means that a
- // mode change is in progress. In that case we shouldn't dispatch an event
- // as it will be dispatched when the current mode changes.
- if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
- return;
- }
-
- // If there is no change from cached mode, there is no need to dispatch an event
- if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
- return;
- }
-
- mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
- onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle,
- mPolicy.cachedModeChangedParams->mode);
-}
-
-void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
if (hasEventThreads()) {
- eventThreadFor(cycle).onModeChanged(mode);
+ eventThreadFor(Cycle::Render).onModeChanged(mode);
+ }
+
+ return isPacesetter;
+}
+
+void Scheduler::emitModeChangeIfNeeded() {
+ if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
+ ALOGW("No mode change to emit");
+ return;
+ }
+
+ const auto& mode = *mPolicy.modeOpt;
+
+ if (mode != pacesetterSelectorPtr()->getActiveMode()) {
+ // A mode change is pending. The event will be emitted when the mode becomes active.
+ return;
+ }
+
+ if (mode == *mPolicy.emittedModeOpt) {
+ // The event was already emitted.
+ return;
+ }
+
+ mPolicy.emittedModeOpt = mode;
+
+ if (hasEventThreads()) {
+ eventThreadFor(Cycle::Render).onModeChanged(mode);
}
}
@@ -908,9 +913,13 @@
}
}
-bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
- std::scoped_lock lock(mPolicyLock);
- return updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate);
+void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ const bool changed = (std::scoped_lock(mPolicyLock),
+ updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate));
+
+ if (changed) {
+ onFrameRateOverridesChanged();
+ }
}
bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
@@ -1130,7 +1139,8 @@
for (auto& [id, choice] : modeChoices) {
modeRequests.emplace_back(
display::DisplayModeRequest{.mode = std::move(choice.mode),
- .emitEvent = !choice.consideredSignals.idle});
+ .emitEvent = choice.consideredSignals
+ .shouldEmitEvent()});
}
if (!FlagManager::getInstance().vrr_bugfix_dropped_frame()) {
@@ -1140,12 +1150,10 @@
if (mPolicy.modeOpt != modeOpt) {
mPolicy.modeOpt = modeOpt;
refreshRateChanged = true;
- } else {
- // We don't need to change the display mode, but we might need to send an event
- // about a mode change, since it was suppressed if previously considered idle.
- if (!consideredSignals.idle) {
- dispatchCachedReportedMode();
- }
+ } else if (consideredSignals.shouldEmitEvent()) {
+ // The mode did not change, but we may need to emit if DisplayModeRequest::emitEvent was
+ // previously false.
+ emitModeChangeIfNeeded();
}
}
if (refreshRateChanged) {
@@ -1158,7 +1166,7 @@
updateFrameRateOverridesLocked(consideredSignals, mPolicy.modeOpt->fps);
}
if (frameRateOverridesChanged) {
- mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ onFrameRateOverridesChanged();
}
return consideredSignals;
}
@@ -1258,6 +1266,8 @@
} else {
mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
}
+
+ onFrameRateOverridesChanged();
}
void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) {
@@ -1276,6 +1286,7 @@
}
mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
+ onFrameRateOverridesChanged();
}
void Scheduler::updateSmallAreaDetection(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index b7b5812..9b32fa9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -154,13 +154,11 @@
void dispatchHotplugError(int32_t errorCode);
- void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock);
- void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&);
+ // Returns true if the PhysicalDisplayId is the pacesetter.
+ bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
- void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId);
-
void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
// Modifies work duration in the event thread.
@@ -326,7 +324,7 @@
return mLayerHistory.getLayerFramerate(now, id);
}
- bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
+ void updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
// Returns true if the small dirty detection is enabled for the appId.
bool supportSmallDirtyDetection(int32_t appId) {
@@ -450,6 +448,9 @@
bool updateFrameRateOverridesLocked(GlobalSignals, Fps displayRefreshRate)
REQUIRES(mPolicyLock);
+
+ void onFrameRateOverridesChanged();
+
void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&,
Fps displayRefreshRate);
int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&,
@@ -457,7 +458,7 @@
void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&,
Fps fps) EXCLUDES(mChoreographerLock);
- void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
+ void emitModeChangeIfNeeded() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
// IEventThreadCallback overrides
bool throttleVsync(TimePoint, uid_t) override;
@@ -583,13 +584,8 @@
// Chosen display mode.
ftl::Optional<FrameRateMode> modeOpt;
- struct ModeChangedParams {
- Cycle cycle;
- FrameRateMode mode;
- };
-
- // Parameters for latest dispatch of mode change event.
- std::optional<ModeChangedParams> cachedModeChangedParams;
+ // Display mode of latest emitted event.
+ std::optional<FrameRateMode> emittedModeOpt;
} mPolicy GUARDED_BY(mPolicyLock);
std::mutex mChoreographerLock;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 04491a2..4a7cff5 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -89,7 +89,9 @@
}
bool VSyncPredictor::validate(nsecs_t timestamp) const {
+ SFTRACE_CALL();
if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
+ SFTRACE_INSTANT("timestamp valid (first)");
return true;
}
@@ -98,7 +100,11 @@
(timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent &&
percent <= (kMaxPercent - kOutlierTolerancePercent)) {
- SFTRACE_FORMAT_INSTANT("timestamp is not aligned with model");
+ SFTRACE_FORMAT_INSTANT("timestamp not aligned with model. aValidTimestamp %.2fms ago"
+ ", timestamp %.2fms ago, idealPeriod=%.2 percent=%d",
+ (mClock->now() - aValidTimestamp) / 1e6f,
+ (mClock->now() - timestamp) / 1e6f,
+ idealPeriod() / 1e6f, percent);
return false;
}
@@ -148,7 +154,10 @@
// Add the timestamp to mTimestamps before clearing it so we could
// update mKnownTimestamp based on the new timestamp.
mTimestamps.push_back(timestamp);
- clearTimestamps();
+
+ // Do not clear timelines as we don't want to break the phase while
+ // we are still learning.
+ clearTimestamps(/* clearTimelines */ false);
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
@@ -235,7 +244,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -245,7 +254,7 @@
auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -425,6 +434,9 @@
timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
std::lock_guard lock(mMutex);
+ // do not clear the timelines on VRR displays if we didn't change the mode
+ const bool isVrr = modePtr->getVrrConfig().has_value();
+ const bool clearTimelines = !isVrr || mDisplayModePtr->getId() != modePtr->getId();
mDisplayModePtr = modePtr;
mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
@@ -438,8 +450,10 @@
mRateMap[idealPeriod()] = {idealPeriod(), 0};
}
- mTimelines.clear();
- clearTimestamps();
+ if (clearTimelines) {
+ mTimelines.clear();
+ }
+ clearTimestamps(clearTimelines);
}
Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
@@ -556,15 +570,19 @@
return mRateMap.find(idealPeriod())->second;
}
-void VSyncPredictor::clearTimestamps() {
- SFTRACE_CALL();
+void VSyncPredictor::clearTimestamps(bool clearTimelines) {
+ SFTRACE_FORMAT("%s: clearTimelines=%d", __func__, clearTimelines);
if (!mTimestamps.empty()) {
auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
if (mKnownTimestamp) {
mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
} else {
mKnownTimestamp = maxRb;
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp (maxRb) was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
}
mTimestamps.clear();
@@ -575,7 +593,7 @@
if (mTimelines.empty()) {
mLastCommittedVsync = TimePoint::fromNs(0);
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
- } else {
+ } else if (clearTimelines) {
while (mTimelines.size() > 1) {
mTimelines.pop_front();
}
@@ -596,9 +614,10 @@
}
void VSyncPredictor::resetModel() {
+ SFTRACE_CALL();
std::lock_guard lock(mMutex);
mRateMap[idealPeriod()] = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
}
void VSyncPredictor::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 6c8a2f2..2df3d04 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -127,7 +127,7 @@
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
- void clearTimestamps() REQUIRES(mMutex);
+ void clearTimestamps(bool clearTimelines) REQUIRES(mMutex);
const std::unique_ptr<Clock> mClock;
const PhysicalDisplayId mId;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 2455822..b974cd2 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -140,7 +140,9 @@
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
+ // kernel idle timer is not applicable for VRR
+ const bool supportKernelIdleTimer = mSupportKernelIdleTimer && !modePtr->getVrrConfig();
+ if (!supportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 8adf2a6..3ee1e54 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -69,7 +69,10 @@
const FenceTimePtr& FrameTarget::presentFenceForPreviousFrame() const {
if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
- return mPresentFences.back().fenceTime;
+ if (mPresentFences.size() > 0) {
+ return mPresentFences.back().fenceTime;
+ }
+ return FenceTime::NO_FENCE;
}
return mPresentFencesLegacy.front().fenceTime;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c34d58c..534df13 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -853,6 +853,8 @@
auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, "");
if (algorithm == "gaussian") {
return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
+ } else if (algorithm == "kawase2") {
+ return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
} else {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
}
@@ -911,9 +913,11 @@
LOG_ALWAYS_FATAL_IF(!configureLocked(),
"Initial display configuration failed: HWC did not hotplug");
+ mActiveDisplayId = getPrimaryDisplayIdLocked();
+
// Commit primary display.
sp<const DisplayDevice> display;
- if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
+ if (const auto indexOpt = mCurrentState.getDisplayIndex(mActiveDisplayId)) {
const auto& displays = mCurrentState.displays;
const auto& token = displays.keyAt(*indexOpt);
@@ -1354,7 +1358,7 @@
}
if (emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, mode);
+ mScheduler->onDisplayModeChanged(displayId, mode);
}
break;
case DesiredModeAction::None:
@@ -1451,7 +1455,7 @@
}
if (pendingModeOpt->emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, activeMode);
+ mScheduler->onDisplayModeChanged(displayId, activeMode);
}
}
@@ -3585,16 +3589,6 @@
mPhysicalDisplays.erase(displayId);
}
-void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
- const scheduler::FrameRateMode& mode) {
- // TODO(b/255635821): Merge code paths and move to Scheduler.
- const auto onDisplayModeChanged = displayId == mActiveDisplayId
- ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
- : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
-
- ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode);
-}
-
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -4178,15 +4172,6 @@
}
}
-void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
- PhysicalDisplayId displayId = [&]() {
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
-}
-
void SurfaceFlinger::notifyCpuLoadUp() {
mPowerAdvisor->notifyCpuLoadUp();
}
@@ -6769,15 +6754,9 @@
return NO_ERROR;
}
case 1039: {
- PhysicalDisplayId displayId = [&]() {
- Mutex::Autolock lock(mStateLock);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- auto inUid = static_cast<uid_t>(data.readInt32());
+ const auto uid = static_cast<uid_t>(data.readInt32());
const auto refreshRate = data.readFloat();
- mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
+ mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{uid, refreshRate});
return NO_ERROR;
}
// Toggle caching feature
@@ -7970,13 +7949,9 @@
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
- // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
- // be depending in this callback.
- if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
- mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
+ if (const bool isPacesetter =
+ mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
toggleKernelIdleTimer();
- } else {
- mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
}
auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -8000,10 +7975,7 @@
setDesiredMode({std::move(preferredMode), .emitEvent = true});
// Update the frameRateOverride list as the display render rate might have changed
- if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) {
- triggerOnFrameRateOverridesChanged();
- }
-
+ mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps);
return NO_ERROR;
}
@@ -8163,13 +8135,7 @@
}
status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
- PhysicalDisplayId displayId = [&]() {
- Mutex::Autolock lock(mStateLock);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
+ mScheduler->setGameModeFrameRateForUid(FrameRateOverride{uid, frameRate});
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9a1ad03..8b71f3b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -697,7 +697,6 @@
void requestHardwareVsync(PhysicalDisplayId, bool) override;
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
- void triggerOnFrameRateOverridesChanged() override;
void onChoreographerAttached() override;
void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
Fps renderRate) override;
@@ -963,18 +962,12 @@
return nullptr;
}
- // Returns the primary display or (for foldables) the active display, assuming that the inner
- // and outer displays have mutually exclusive power states.
+ // Returns the primary display or (for foldables) the active display.
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
}
sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
- return display;
- }
- // The active display is outdated, so fall back to the primary display.
- mActiveDisplayId = getPrimaryDisplayIdLocked();
return getDisplayDeviceLocked(mActiveDisplayId);
}
@@ -1074,8 +1067,6 @@
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&);
-
/*
* VSYNC
*/
@@ -1315,7 +1306,7 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
- // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+ // The inner or outer display for foldables, while unfolded or folded, respectively.
std::atomic<PhysicalDisplayId> mActiveDisplayId;
display::DisplayModeController mDisplayModeController;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 0bafb71..b189598 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -62,12 +62,12 @@
proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size()));
for (auto& layerState : t.states) {
- proto.mutable_layer_changes()->Add(std::move(toProto(layerState)));
+ proto.mutable_layer_changes()->Add(toProto(layerState));
}
proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size()));
for (auto& displayState : t.displays) {
- proto.mutable_display_changes()->Add(std::move(toProto(displayState)));
+ proto.mutable_display_changes()->Add(toProto(displayState));
}
proto.mutable_merged_transaction_ids()->Reserve(
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 696f348..bc9f809 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -244,7 +244,7 @@
static_cast<int32_t>(update.createdLayers.size()));
for (const auto& args : update.createdLayers) {
- entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args)));
+ entryProto.mutable_added_layers()->Add(mProtoParser.toProto(args));
}
entryProto.mutable_destroyed_layers()->Reserve(
@@ -276,7 +276,7 @@
static_cast<int32_t>(update.displayInfos.size()));
for (auto& [layerStack, displayInfo] : update.displayInfos) {
entryProto.mutable_displays()->Add(
- std::move(mProtoParser.toProto(displayInfo, layerStack.id)));
+ mProtoParser.toProto(displayInfo, layerStack.id));
}
}
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
index 0d7ac9b..dc5716b 100644
--- a/services/surfaceflinger/common/include/common/trace.h
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -48,7 +48,7 @@
::tracing_perfetto::traceAsyncBegin(ATRACE_TAG, name, cookie)
#define SFTRACE_ASYNC_END(name, cookie) ::tracing_perfetto::traceAsyncEnd(ATRACE_TAG, name, cookie)
#define SFTRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
- ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, track_name, name, cookie)
+ ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, name, track_name, cookie)
#define SFTRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
::tracing_perfetto::traceAsyncEndForTrack(ATRACE_TAG, track_name, cookie)
#define SFTRACE_INSTANT(name) ::tracing_perfetto::traceInstant(ATRACE_TAG, name)
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 125a4e4..358f6b0 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -78,6 +78,8 @@
SchedulerTest();
+ static constexpr RefreshRateSelector::LayerRequirement kLayer = {.weight = 1.f};
+
static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
@@ -85,6 +87,9 @@
ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
+ static inline FrameRateMode kDisplay1Mode60_60{60_Hz, kDisplay1Mode60};
+ static inline FrameRateMode kDisplay1Mode120_120{120_Hz, kDisplay1Mode120};
+
static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
@@ -123,6 +128,7 @@
.WillRepeatedly(Return(mEventThreadConnection));
mScheduler->setEventThread(Cycle::Render, std::move(eventThread));
+ mScheduler->setEventThread(Cycle::LastComposite, std::make_unique<MockEventThread>());
mFlinger.resetScheduler(mScheduler);
}
@@ -212,11 +218,39 @@
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
-TEST_F(SchedulerTest, dispatchCachedReportedMode) {
- mScheduler->clearCachedReportedMode();
+TEST_F(SchedulerTest, emitModeChangeEvent) {
+ const auto selectorPtr =
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId());
+ mScheduler->registerDisplay(kDisplayId1, selectorPtr);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+ mScheduler->setContentRequirements({kLayer});
+
+ // No event is emitted in response to idle.
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
- EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+
+ using TimerState = TestableScheduler::TimerState;
+
+ mScheduler->idleTimerCallback(TimerState::Expired);
+ selectorPtr->setActiveMode(kDisplay1Mode60->getId(), 60_Hz);
+
+ auto layer = kLayer;
+ layer.vote = RefreshRateSelector::LayerVoteType::ExplicitExact;
+ layer.desiredRefreshRate = 60_Hz;
+ mScheduler->setContentRequirements({layer});
+
+ // An event is emitted implicitly despite choosing the same mode as when idle.
+ EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode60_60)).Times(1);
+
+ mScheduler->idleTimerCallback(TimerState::Reset);
+
+ mScheduler->setContentRequirements({kLayer});
+
+ // An event is emitted explicitly for the mode change.
+ EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1);
+
+ mScheduler->touchTimerCallback(TimerState::Reset);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
@@ -272,14 +306,12 @@
/*updateAttachedChoreographer*/ false);
}
-TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
+TEST_F(SchedulerTest, chooseDisplayModes) {
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
kDisplay1Mode60->getId()));
- std::vector<RefreshRateSelector::LayerRequirement> layers =
- std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
- mScheduler->setContentRequirements(layers);
+ mScheduler->setContentRequirements({kLayer, kLayer});
GlobalSignals globalSignals = {.idle = true};
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -314,15 +346,14 @@
EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
}
-TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) {
+TEST_F(SchedulerTest, chooseDisplayModesHighHintTouchSignal) {
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
kDisplay1Mode60->getId()));
using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
- std::vector<RefreshRateSelector::LayerRequirement> layers =
- std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+ std::vector<RefreshRateSelector::LayerRequirement> layers = {kLayer, kLayer};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
@@ -397,9 +428,7 @@
kDisplay2Mode60},
GlobalSignals{});
- std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
- {.weight = 1.f}};
- mScheduler->setContentRequirements(layers);
+ mScheduler->setContentRequirements({kLayer, kLayer});
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 0814e3d..df16b2e 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -176,6 +176,11 @@
mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
}
+ using Scheduler::TimerState;
+
+ using Scheduler::idleTimerCallback;
+ using Scheduler::touchTimerCallback;
+
void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) {
std::lock_guard<std::mutex> lock(mPolicyLock);
mPolicy.contentRequirements = std::move(layers);
@@ -188,15 +193,7 @@
return Scheduler::chooseDisplayModes();
}
- void dispatchCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
- Scheduler::dispatchCachedReportedMode();
- }
-
- void clearCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
- mPolicy.cachedModeChangedParams.reset();
- }
+ using Scheduler::onDisplayModeChanged;
void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) {
auto schedule = getVsyncSchedule(id);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index fb4ef70..7bf1674 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -131,14 +131,14 @@
// add layers and add some layer transaction
{
frontend::Update update;
- update.layerCreationArgs.emplace_back(std::move(
+ update.layerCreationArgs.emplace_back(
getLayerCreationArgs(mParentLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
/*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
- /*addToRoot=*/true)));
- update.layerCreationArgs.emplace_back(std::move(
+ /*addToRoot=*/true));
+ update.layerCreationArgs.emplace_back(
getLayerCreationArgs(mChildLayerId, mParentLayerId,
/*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
- /*addToRoot=*/true)));
+ /*addToRoot=*/true));
TransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 8f21cdb..d45cc66 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -26,7 +26,6 @@
MOCK_METHOD(void, requestHardwareVsync, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
- MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
MOCK_METHOD(void, onChoreographerAttached, (), (override));
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
@@ -38,7 +37,6 @@
void requestHardwareVsync(PhysicalDisplayId, bool) override {}
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
- void triggerOnFrameRateOverridesChanged() override {}
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
void onCommitNotComposited(PhysicalDisplayId) override {}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 3f89960..ef213f0 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -339,10 +339,13 @@
ALOGD("Unload builtin Vulkan driver.");
- // Close the opened device
- int err = hal_.dev_->common.close(
- const_cast<struct hw_device_t*>(&hal_.dev_->common));
- ALOG_ASSERT(!err, "hw_device_t::close() failed.");
+ if (hal_.dev_->common.close != nullptr)
+ {
+ // Close the opened device
+ int err = hal_.dev_->common.close(
+ const_cast<struct hw_device_t*>(&hal_.dev_->common));
+ ALOG_ASSERT(!err, "hw_device_t::close() failed.");
+ }
// Close the opened shared library in the hw_module_t
android_unload_sphal_library(hal_.dev_->common.module->dso);