Merge "Revert "Revert "Load native GLES driver when specified.""" into udc-qpr-dev
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index 03dfd63..39772ae 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -16,5 +16,20 @@
<motion-predictor>
<!-- The time interval (ns) between the model's predictions. -->
<prediction-interval>4166666</prediction-interval> <!-- 4.167 ms = ~240 Hz -->
+ <!-- The noise floor (px) for predicted distances.
+
+ As the model is trained stochastically, there is some expected minimum
+ variability in its output. This can be a UX issue when the input device
+ is moving slowly and the variability is large relative to the magnitude
+ of the motion. In these cases, it is better to inhibit the prediction,
+ rather than show noisy predictions (and there is little benefit to
+ prediction anyway).
+
+ The value for this parameter should at least be close to the maximum
+ predicted distance when the input device is held stationary (i.e. the
+ expected minimum variability), and perhaps a little larger to capture
+ the UX issue mentioned above.
+ -->
+ <distance-noise-floor>0.2</distance-noise-floor>
</motion-predictor>
diff --git a/data/etc/input/motion_predictor_model.tflite b/data/etc/input/motion_predictor_model.tflite
index 10b3c8b..45fc162 100644
--- a/data/etc/input/motion_predictor_model.tflite
+++ b/data/etc/input/motion_predictor_model.tflite
Binary files differ
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index fbd6026..2edc138 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -99,6 +99,14 @@
// A TFLite model for generating motion predictions.
class TfLiteMotionPredictorModel {
public:
+ struct Config {
+ // The time between predictions.
+ nsecs_t predictionInterval = 0;
+ // The noise floor for predictions.
+ // Distances (r) less than this should be discarded as noise.
+ float distanceNoiseFloor = 0;
+ };
+
// Creates a model from an encoded Flatbuffer model.
static std::unique_ptr<TfLiteMotionPredictorModel> create();
@@ -110,8 +118,7 @@
// Returns the length of the model's output buffers.
size_t outputLength() const;
- // Returns the time interval between predictions.
- nsecs_t predictionInterval() const { return mPredictionInterval; }
+ const Config& config() const { return mConfig; }
// Executes the model.
// Returns true if the model successfully executed and the output tensors can be read.
@@ -132,7 +139,7 @@
private:
explicit TfLiteMotionPredictorModel(std::unique_ptr<android::base::MappedFile> model,
- nsecs_t predictionInterval);
+ Config config);
void allocateTensors();
void attachInputTensors();
@@ -154,7 +161,7 @@
std::unique_ptr<tflite::Interpreter> mInterpreter;
tflite::SignatureRunner* mRunner = nullptr;
- const nsecs_t mPredictionInterval = 0;
+ const Config mConfig = {};
};
} // namespace android
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index bf34987..3c8df2b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -73,6 +73,7 @@
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
+ "android/gui/IWindowInfosPublisher.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfo.aidl",
"android/gui/WindowInfosUpdate.aidl",
@@ -90,6 +91,7 @@
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
+ "android/gui/IWindowInfosPublisher.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfosUpdate.aidl",
"android/gui/WindowInfo.aidl",
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 76e7b6e..0929b8e 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -22,7 +22,6 @@
namespace android {
using gui::DisplayInfo;
-using gui::IWindowInfosReportedListener;
using gui::WindowInfo;
using gui::WindowInfosListener;
using gui::aidl_utils::statusTFromBinderStatus;
@@ -40,8 +39,13 @@
{
std::scoped_lock lock(mListenersMutex);
if (mWindowInfosListeners.empty()) {
- binder::Status s = surfaceComposer->addWindowInfosListener(this);
+ gui::WindowInfosListenerInfo listenerInfo;
+ binder::Status s = surfaceComposer->addWindowInfosListener(this, &listenerInfo);
status = statusTFromBinderStatus(s);
+ if (status == OK) {
+ mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
+ mListenerId = listenerInfo.listenerId;
+ }
}
if (status == OK) {
@@ -85,8 +89,7 @@
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const gui::WindowInfosUpdate& update,
- const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
+ const gui::WindowInfosUpdate& update) {
std::unordered_set<sp<WindowInfosListener>, gui::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -104,9 +107,7 @@
listener->onWindowInfosChanged(update);
}
- if (windowInfosReportedListener) {
- windowInfosReportedListener->onWindowInfosReported();
- }
+ mWindowInfosPublisher->ackWindowInfosReceived(update.vsyncId, mListenerId);
return binder::Status::ok();
}
@@ -114,7 +115,10 @@
void WindowInfosListenerReporter::reconnect(const sp<gui::ISurfaceComposer>& composerService) {
std::scoped_lock lock(mListenersMutex);
if (!mWindowInfosListeners.empty()) {
- composerService->addWindowInfosListener(this);
+ gui::WindowInfosListenerInfo listenerInfo;
+ composerService->addWindowInfosListener(this, &listenerInfo);
+ mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
+ mListenerId = listenerInfo.listenerId;
}
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index ec3266c..539a1c1 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -40,12 +40,14 @@
import android.gui.ISurfaceComposerClient;
import android.gui.ITunnelModeEnabledListener;
import android.gui.IWindowInfosListener;
+import android.gui.IWindowInfosPublisher;
import android.gui.LayerCaptureArgs;
import android.gui.LayerDebugInfo;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
import android.gui.StaticDisplayInfo;
+import android.gui.WindowInfosListenerInfo;
/** @hide */
interface ISurfaceComposer {
@@ -500,7 +502,7 @@
*/
int getMaxAcquiredBufferCount();
- void addWindowInfosListener(IWindowInfosListener windowInfosListener);
+ WindowInfosListenerInfo addWindowInfosListener(IWindowInfosListener windowInfosListener);
void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
diff --git a/libs/gui/aidl/android/gui/WindowInfosListenerInfo.aidl b/libs/gui/aidl/android/gui/WindowInfosListenerInfo.aidl
new file mode 100644
index 0000000..0ca13b7
--- /dev/null
+++ b/libs/gui/aidl/android/gui/WindowInfosListenerInfo.aidl
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.IWindowInfosPublisher;
+
+/** @hide */
+parcelable WindowInfosListenerInfo {
+ long listenerId;
+ IWindowInfosPublisher windowInfosPublisher;
+}
\ No newline at end of file
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index 400229d..07cb5ed 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,11 +16,9 @@
package android.gui;
-import android.gui.IWindowInfosReportedListener;
import android.gui.WindowInfosUpdate;
/** @hide */
oneway interface IWindowInfosListener {
- void onWindowInfosChanged(
- in WindowInfosUpdate update, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+ void onWindowInfosChanged(in WindowInfosUpdate update);
}
diff --git a/libs/gui/android/gui/IWindowInfosPublisher.aidl b/libs/gui/android/gui/IWindowInfosPublisher.aidl
new file mode 100644
index 0000000..5a9c328
--- /dev/null
+++ b/libs/gui/android/gui/IWindowInfosPublisher.aidl
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IWindowInfosPublisher
+{
+ void ackWindowInfosReceived(long vsyncId, long listenerId);
+}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 8c003d8..4c7d056 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -153,8 +153,8 @@
MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
- MOCK_METHOD(binder::Status, addWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
- (override));
+ MOCK_METHOD(binder::Status, addWindowInfosListener,
+ (const sp<gui::IWindowInfosListener>&, gui::WindowInfosListenerInfo*), (override));
MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 7c150d5..3ff6735 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -26,6 +26,7 @@
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
+#include <android/gui/IWindowInfosPublisher.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
#include <gui/ITransactionCompletedListener.h>
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 38cb108..684e21a 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -18,7 +18,7 @@
#include <android/gui/BnWindowInfosListener.h>
#include <android/gui/ISurfaceComposer.h>
-#include <android/gui/IWindowInfosReportedListener.h>
+#include <android/gui/IWindowInfosPublisher.h>
#include <binder/IBinder.h>
#include <gui/SpHash.h>
#include <gui/WindowInfosListener.h>
@@ -30,8 +30,7 @@
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update,
- const sp<gui::IWindowInfosReportedListener>&) override;
+ binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update) override;
status_t addWindowInfosListener(
const sp<gui::WindowInfosListener>& windowInfosListener,
const sp<gui::ISurfaceComposer>&,
@@ -47,5 +46,8 @@
std::vector<gui::WindowInfo> mLastWindowInfos GUARDED_BY(mListenersMutex);
std::vector<gui::DisplayInfo> mLastDisplayInfos GUARDED_BY(mListenersMutex);
+
+ sp<gui::IWindowInfosPublisher> mWindowInfosPublisher;
+ int64_t mListenerId;
};
} // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 90c0a63..567604d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1002,7 +1002,8 @@
}
binder::Status addWindowInfosListener(
- const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) override {
+ const sp<gui::IWindowInfosListener>& /*windowInfosListener*/,
+ gui::WindowInfosListenerInfo* /*outInfo*/) override {
return binder::Status::ok();
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 68e6888..c2ea35c 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -138,7 +138,8 @@
// Pass input event to the MetricsManager.
if (!mMetricsManager) {
mMetricsManager =
- std::make_optional<MotionPredictorMetricsManager>(mModel->predictionInterval(),
+ std::make_optional<MotionPredictorMetricsManager>(mModel->config()
+ .predictionInterval,
mModel->outputLength());
}
mMetricsManager->onRecord(event);
@@ -184,8 +185,18 @@
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
- // TODO(b/266747654): Stop predictions if confidence and/or predicted pressure are below
- // some thresholds.
+ if (predictedR[i] < mModel->config().distanceNoiseFloor) {
+ // Stop predicting when the predicted output is below the model's noise floor.
+ //
+ // We assume that all subsequent predictions in the batch are unreliable because later
+ // predictions are conditional on earlier predictions, and a state of noise is not a
+ // good basis for prediction.
+ //
+ // The UX trade-off is that this potentially sacrifices some predictions when the input
+ // device starts to speed up, but avoids producing noisy predictions as it slows down.
+ break;
+ }
+ // TODO(b/266747654): Stop predictions if confidence is < some threshold.
const TfLiteMotionPredictorSample::Point predictedPoint =
convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
@@ -197,7 +208,7 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, predictedPoint.y);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
- predictionTime += mModel->predictionInterval();
+ predictionTime += mModel->config().predictionInterval;
if (i == 0) {
hasPredictions = true;
prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 9f4aaa8..5984b4d3 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -100,6 +100,16 @@
return value;
}
+float parseXMLFloat(const tinyxml2::XMLElement& configRoot, const char* elementName) {
+ const tinyxml2::XMLElement* element = configRoot.FirstChildElement(elementName);
+ LOG_ALWAYS_FATAL_IF(!element, "Could not find '%s' element", elementName);
+
+ float value = 0;
+ LOG_ALWAYS_FATAL_IF(element->QueryFloatText(&value) != tinyxml2::XML_SUCCESS,
+ "Failed to parse %s: %s", elementName, element->GetText());
+ return value;
+}
+
// A TFLite ErrorReporter that logs to logcat.
class LoggingErrorReporter : public tflite::ErrorReporter {
public:
@@ -152,6 +162,7 @@
::tflite::ops::builtin::Register_CONCATENATION());
resolver->AddBuiltin(::tflite::BuiltinOperator_FULLY_CONNECTED,
::tflite::ops::builtin::Register_FULLY_CONNECTED());
+ resolver->AddBuiltin(::tflite::BuiltinOperator_GELU, ::tflite::ops::builtin::Register_GELU());
return resolver;
}
@@ -208,13 +219,7 @@
float phi = 0;
float orientation = 0;
- // Ignore the sample if there is no movement. These samples can occur when there's change to a
- // property other than the coordinates and pollute the input to the model.
- if (r == 0) {
- return;
- }
-
- if (!mAxisFrom) { // Second point.
+ if (!mAxisFrom && r > 0) { // Second point.
// We can only determine the distance from the first point, and not any
// angle. However, if the second point forms an axis, the orientation can
// be transformed relative to that axis.
@@ -235,8 +240,10 @@
}
// Update the axis for the next point.
- mAxisFrom = mAxisTo;
- mAxisTo = sample;
+ if (r > 0) {
+ mAxisFrom = mAxisTo;
+ mAxisTo = sample;
+ }
// Push the current sample onto the end of the input buffers.
mInputR.pushBack(r);
@@ -272,15 +279,18 @@
// Parse configuration file.
const tinyxml2::XMLElement* configRoot = configDocument.FirstChildElement("motion-predictor");
LOG_ALWAYS_FATAL_IF(!configRoot);
- const nsecs_t predictionInterval = parseXMLInt64(*configRoot, "prediction-interval");
+ Config config{
+ .predictionInterval = parseXMLInt64(*configRoot, "prediction-interval"),
+ .distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
+ };
return std::unique_ptr<TfLiteMotionPredictorModel>(
- new TfLiteMotionPredictorModel(std::move(modelBuffer), predictionInterval));
+ new TfLiteMotionPredictorModel(std::move(modelBuffer), std::move(config)));
}
TfLiteMotionPredictorModel::TfLiteMotionPredictorModel(
- std::unique_ptr<android::base::MappedFile> model, nsecs_t predictionInterval)
- : mFlatBuffer(std::move(model)), mPredictionInterval(predictionInterval) {
+ std::unique_ptr<android::base::MappedFile> model, Config config)
+ : mFlatBuffer(std::move(model)), mConfig(std::move(config)) {
CHECK(mFlatBuffer);
mErrorReporter = std::make_unique<LoggingErrorReporter>();
mModel = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(mFlatBuffer->data(),
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 7a62f5e..4ac7ae9 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -72,11 +72,20 @@
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
+TEST(MotionPredictorTest, StationaryNoiseFloor) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
+ []() { return true /*enable prediction*/; });
+ predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
+ predictor.record(getMotionEvent(MOVE, 0, 1, 35ms)); // No movement.
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_EQ(nullptr, predicted);
+}
+
TEST(MotionPredictorTest, Offset) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
[]() { return true /*enable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
- predictor.record(getMotionEvent(MOVE, 0, 2, 35ms));
+ predictor.record(getMotionEvent(MOVE, 0, 5, 35ms)); // Move enough to overcome the noise floor.
std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
ASSERT_NE(nullptr, predicted);
ASSERT_GE(predicted->getEventTime(), 41);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 18d9864..0212e08 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -713,7 +713,9 @@
SkCanvas* canvas = dstCanvas;
SkiaCapture::OffscreenState offscreenCaptureState;
const LayerSettings* blurCompositionLayer = nullptr;
- if (mBlurFilter) {
+
+ // TODO (b/270314344): Enable blurs in protected context.
+ if (mBlurFilter && !mInProtectedContext) {
bool requiresCompositionLayer = false;
for (const auto& layer : layers) {
// if the layer doesn't have blur or it is not visible then continue
@@ -807,7 +809,8 @@
const auto [bounds, roundRectClip] =
getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
layer.geometry.roundedCornersRadius);
- if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
+ // TODO (b/270314344): Enable blurs in protected context.
+ if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha) && !mInProtectedContext) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
// if multiple layers have blur, then we need to take a snapshot now because
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index a93a2ea..88e1d7d 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -447,6 +447,9 @@
const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
/* Notifies the input reader policy that a stylus gesture has started. */
virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
+
+ /* Returns true if any InputConnection is currently active. */
+ virtual bool isInputMethodConnectionActive() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ea95f78..08600b2 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1040,6 +1040,16 @@
return mReader->getLedMetaStateLocked();
}
+void InputReader::ContextImpl::setPreventingTouchpadTaps(bool prevent) {
+ // lock is already held by the input loop
+ mReader->mPreventingTouchpadTaps = prevent;
+}
+
+bool InputReader::ContextImpl::isPreventingTouchpadTaps() {
+ // lock is already held by the input loop
+ return mReader->mPreventingTouchpadTaps;
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9112913..01ec7c1 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -155,6 +155,9 @@
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+ void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
+ REQUIRES(mLock) override;
+ bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
} mContext;
friend class ContextImpl;
@@ -185,6 +188,9 @@
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
+ // true if tap-to-click on touchpad currently disabled
+ bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 0beace1..aed7563 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -62,6 +62,9 @@
virtual void updateLedMetaState(int32_t metaState) = 0;
virtual int32_t getLedMetaState() = 0;
+
+ virtual void setPreventingTouchpadTaps(bool prevent) = 0;
+ virtual bool isPreventingTouchpadTaps() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 7388752..5c42e10 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -242,6 +242,7 @@
keyDown.downTime = when;
mKeyDowns.push_back(keyDown);
}
+ onKeyDownProcessed();
} else {
// Remove key down.
if (keyDownIndex) {
@@ -419,4 +420,19 @@
return out;
}
+void KeyboardInputMapper::onKeyDownProcessed() {
+ InputReaderContext& context = *getContext();
+ if (context.isPreventingTouchpadTaps()) {
+ // avoid pinging java service unnecessarily
+ return;
+ }
+ // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
+ // shortcuts
+ bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode);
+ if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
+ context.fadePointer();
+ context.setPreventingTouchpadTaps(true);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index cd3d3c4..96044eb 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -104,6 +104,7 @@
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
+ void onKeyDownProcessed();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index e826341..3abf2bd 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -153,6 +153,9 @@
const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
+ if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
+ enableTapToClick();
+ }
rotateDelta(mOrientation, &deltaX, &deltaY);
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -191,6 +194,15 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+
+ if (mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ if (gesture.details.buttons.is_tap) {
+ // return early to prevent this tap
+ return out;
+ }
+ }
+
const uint32_t buttonsPressed = gesture.details.buttons.down;
bool pointerDown = isPointerDown(mButtonState) ||
buttonsPressed &
@@ -337,6 +349,9 @@
// magnitude, which will also result in the pointer icon being updated.
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
// initiated with a touchpad.
+ if (!mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ }
return {handleMove(when, readTime,
Gesture(kGestureMove, gesture.start_time, gesture.end_time,
/*dx=*/0.f,
@@ -545,7 +560,7 @@
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
- /* flags= */ 0,
+ /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
mReaderContext.getGlobalMetaState(),
buttonState,
mCurrentClassification,
@@ -561,4 +576,8 @@
/* videoFrames= */ {}};
}
+void GestureConverter::enableTapToClick() {
+ mReaderContext.setPreventingTouchpadTaps(false);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index b613b88..3ea3790 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -78,6 +78,8 @@
const PointerCoords* pointerCoords, float xCursorPosition,
float yCursorPosition);
+ void enableTapToClick();
+
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 300bb85..1585fdd 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -62,6 +62,7 @@
"SyncQueue_test.cpp",
"TestInputListener.cpp",
"TouchpadInputMapper_test.cpp",
+ "KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
],
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 3486d0f..30222bf 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -209,6 +209,14 @@
mConfig.stylusPointerIconEnabled = enabled;
}
+void FakeInputReaderPolicy::setIsInputMethodConnectionActive(bool active) {
+ mIsInputMethodConnectionActive = active;
+}
+
+bool FakeInputReaderPolicy::isInputMethodConnectionActive() {
+ return mIsInputMethodConnectionActive;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 85ff01a..78bb2c3 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -77,6 +77,8 @@
void setVelocityControlParams(const VelocityControlParameters& params);
void setStylusButtonMotionEventsEnabled(bool enabled);
void setStylusPointerIconEnabled(bool enabled);
+ void setIsInputMethodConnectionActive(bool active);
+ bool isInputMethodConnectionActive() override;
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
@@ -99,6 +101,7 @@
std::vector<DisplayViewport> mViewports;
TouchAffineTransformation transform;
std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
+ bool mIsInputMethodConnectionActive{false};
uint32_t mNextPointerCaptureSequenceNumber{0};
};
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 482a266..4df0f69 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -983,4 +983,226 @@
ASSERT_TRUE(mFakePointerController->isPointerShown());
}
+TEST_F(GestureConverterTest, Tap) {
+ // Tap should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ ASSERT_EQ(5u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+}
+
+TEST_F(GestureConverterTest, Click) {
+ // Click should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+}
+
+TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
+ // Click should still produce button press/release events
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
+ // initially disable tap-to-click
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index 7f8d556..fef58ec 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -103,12 +103,16 @@
mExternalStylusDevices = devices;
}
+ void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; }
+ bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; }
+
private:
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
std::optional<nsecs_t> mRequestedTimeout;
std::vector<InputDeviceInfo> mExternalStylusDevices;
+ bool mPreventingTouchpadTaps{false};
} mFakeContext;
friend class InputReaderTest;
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index d720a90..b6720c5 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -49,6 +49,9 @@
MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override));
MOCK_METHOD(int32_t, getLedMetaState, (), (override));
+
+ MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
+ MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
};
class MockEventHubInterface : public EventHubInterface {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
new file mode 100644
index 0000000..08a5559
--- /dev/null
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyboardInputMapper.h"
+
+#include <gtest/gtest.h>
+
+#include "InputMapperTest.h"
+#include "InterfaceMocks.h"
+
+#define TAG "KeyboardInputMapper_test"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+/**
+ * Unit tests for KeyboardInputMapper.
+ */
+class KeyboardInputMapperUnitTest : public InputMapperUnitTest {
+protected:
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0},
+ {KEY_A, AKEYCODE_A},
+ {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT},
+ {KEY_LEFTALT, AKEYCODE_ALT_LEFT},
+ {KEY_RIGHTALT, AKEYCODE_ALT_RIGHT},
+ {KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT},
+ {KEY_RIGHTSHIFT, AKEYCODE_SHIFT_RIGHT},
+ {KEY_FN, AKEYCODE_FUNCTION},
+ {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT},
+ {KEY_RIGHTCTRL, AKEYCODE_CTRL_RIGHT},
+ {KEY_LEFTMETA, AKEYCODE_META_LEFT},
+ {KEY_RIGHTMETA, AKEYCODE_META_RIGHT},
+ {KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK},
+ {KEY_NUMLOCK, AKEYCODE_NUM_LOCK},
+ {KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK}};
+
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+
+ // set key-codes expected in tests
+ for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _))
+ .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR)));
+ }
+
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get()));
+
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ }
+
+ void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) {
+ EXPECT_CALL(mMockInputReaderContext, fadePointer)
+ .Times(expectVisible ? 0 : keyCodes.size());
+ for (int32_t keyCode : keyCodes) {
+ process(EV_KEY, keyCode, 1);
+ process(EV_SYN, SYN_REPORT, 0);
+ process(EV_KEY, keyCode, 0);
+ process(EV_SYN, SYN_REPORT, 0);
+ }
+ }
+
+ void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
+ const bool expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).Times(keyCodes.size());
+ if (expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
+ .Times(keyCodes.size());
+ }
+ for (int32_t keyCode : keyCodes) {
+ process(EV_KEY, keyCode, 1);
+ process(EV_SYN, SYN_REPORT, 0);
+ process(EV_KEY, keyCode, 0);
+ process(EV_SYN, SYN_REPORT, 0);
+ }
+ }
+};
+
+/**
+ * Pointer visibility should remain unaffected if there is no active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDoesNotHidePointer) {
+ testPointerVisibilityForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectVisible= */ true);
+}
+
+/**
+ * Pointer should hide if there is a active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionHidePointer) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
+}
+
+/**
+ * Pointer visibility should remain unaffected by meta keys even if Input Method Connection is
+ * active
+ */
+TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDoesNotHidePointer) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
+ KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
+ KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
+ testPointerVisibilityForKeys(metaKeys, /* expectVisible= */ true);
+}
+
+/**
+ * Touchpad tap should not be disabled if there is no active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
+ testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false);
+}
+
+/**
+ * Touchpad tap should be disabled if there is a active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true);
+}
+
+/**
+ * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active
+ */
+TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
+ KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
+ KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
+ testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 1e44e0f..1ecaa64 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -289,6 +289,7 @@
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+ bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -339,6 +340,9 @@
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+
+ void setPreventingTouchpadTaps(bool prevent) {}
+ bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
};
} // namespace android
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index f5b360f..e60db93 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -308,8 +308,12 @@
}
int32_t token;
- mSensors->configDirectReport(sensorHandle, channelHandle, rate, &token);
- return token;
+ status_t status = convertToStatus(
+ mSensors->configDirectReport(sensorHandle, channelHandle, rate, &token));
+ if (status == OK && rate != ISensors::RateLevel::STOP) {
+ status = static_cast<status_t>(token);
+ }
+ return status;
}
void AidlSensorHalWrapper::writeWakeLockHandled(uint32_t count) {
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
index 6ddf790..5a1ec6f 100644
--- a/services/surfaceflinger/BackgroundExecutor.cpp
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -20,6 +20,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <utils/Log.h>
+#include <mutex>
#include "BackgroundExecutor.h"
@@ -60,4 +61,17 @@
LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
}
+void BackgroundExecutor::flushQueue() {
+ std::mutex mutex;
+ std::condition_variable cv;
+ bool flushComplete = false;
+ sendCallbacks({[&]() {
+ std::scoped_lock lock{mutex};
+ flushComplete = true;
+ cv.notify_one();
+ }});
+ std::unique_lock<std::mutex> lock{mutex};
+ cv.wait(lock, [&]() { return flushComplete; });
+}
+
} // namespace android
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
index 0fae5a5..66b7d7a 100644
--- a/services/surfaceflinger/BackgroundExecutor.h
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -34,6 +34,7 @@
// Queues callbacks onto a work queue to be executed by a background thread.
// This is safe to call from multiple threads.
void sendCallbacks(Callbacks&& tasks);
+ void flushQueue();
private:
sem_t mSemaphore;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index f136e9f..c44e22e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -302,6 +302,19 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
+ using fps_approx_ops::operator<;
+ if (refreshRate < 60_Hz) {
+ const bool favorsAtLeast60 =
+ std::find_if(mFrameRatesThatFavorsAtLeast60.begin(),
+ mFrameRatesThatFavorsAtLeast60.end(), [&](Fps fps) {
+ using fps_approx_ops::operator==;
+ return fps == layer.desiredRefreshRate;
+ }) != mFrameRatesThatFavorsAtLeast60.end();
+ if (favorsAtLeast60) {
+ return 0;
+ }
+ }
+
const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
// We only want to score this layer as a fractional pair if the content is not
@@ -1221,10 +1234,19 @@
(supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
};
- const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ if (frameRateModes.empty()) {
+ ALOGW("No matching frame rate modes for %s range. policy: %s", rangeName,
+ policy->toString().c_str());
+ // TODO(b/292105422): Ideally DisplayManager should not send render ranges smaller than
+ // the min supported. See b/292047939.
+ // For not we just ignore the render ranges.
+ frameRateModes = createFrameRateModes(filterModes, {});
+ }
LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
- "No matching frame rate modes for %s range. policy: %s", rangeName,
- policy->toString().c_str());
+ "No matching frame rate modes for %s range even after ignoring the "
+ "render range. policy: %s",
+ rangeName, policy->toString().c_str());
const auto stringifyModes = [&] {
std::string str;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 5052e6e..7af8d03 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -18,6 +18,7 @@
#include <algorithm>
#include <numeric>
+#include <set>
#include <type_traits>
#include <utility>
#include <variant>
@@ -500,6 +501,12 @@
const std::vector<Fps> mKnownFrameRates;
const Config mConfig;
+
+ // A list of known frame rates that favors at least 60Hz if there is no exact match display
+ // refresh rate
+ const std::vector<Fps> mFrameRatesThatFavorsAtLeast60 = {23.976_Hz, 25_Hz, 29.97_Hz, 50_Hz,
+ 59.94_Hz};
+
Config::FrameRateOverride mFrameRateOverrideConfig;
struct GetRankedFrameRatesCache {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 41639b6..0dca21e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/concat.h>
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/small_map.h>
@@ -130,8 +131,8 @@
auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
std::scoped_lock lock(mDisplayLock);
const bool isNew = mDisplays
- .emplace_or_replace(displayId, std::move(selectorPtr),
- std::move(schedulePtr))
+ .emplace_or_replace(displayId, displayId, std::move(selectorPtr),
+ std::move(schedulePtr), mFeatures)
.second;
return std::make_pair(promotePacesetterDisplayLocked(), isNew);
@@ -171,21 +172,43 @@
void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
TimePoint expectedVsyncTime) {
- mPacesetterFrameTargeter.beginFrame({.frameBeginTime = SchedulerClock::now(),
- .vsyncId = vsyncId,
- .expectedVsyncTime = expectedVsyncTime,
- .sfWorkDuration =
- mVsyncModulator->getVsyncConfig().sfWorkDuration},
- *getVsyncSchedule());
+ const FrameTargeter::BeginFrameArgs beginFrameArgs =
+ {.frameBeginTime = SchedulerClock::now(),
+ .vsyncId = vsyncId,
+ // TODO(b/255601557): Calculate per display.
+ .expectedVsyncTime = expectedVsyncTime,
+ .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration};
- if (!compositor.commit(mPacesetterFrameTargeter.target())) {
- return;
+ LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId);
+ const auto pacesetterId = *mPacesetterDisplayId;
+ const auto pacesetterOpt = mDisplays.get(pacesetterId);
+
+ FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr;
+ pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr);
+
+ if (!compositor.commit(pacesetterTargeter.target())) return;
+
+ // TODO(b/256196556): Choose the frontrunner display.
+ FrameTargeters targeters;
+ targeters.try_emplace(pacesetterId, &pacesetterTargeter);
+
+ for (auto& [id, display] : mDisplays) {
+ if (id == pacesetterId) continue;
+
+ FrameTargeter& targeter = *display.targeterPtr;
+ targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
+
+ targeters.try_emplace(id, &targeter);
}
- const auto compositeResult = compositor.composite(mPacesetterFrameTargeter);
+ const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
compositor.sample();
- mPacesetterFrameTargeter.endFrame(compositeResult);
+ for (const auto& [id, targeter] : targeters) {
+ const auto resultOpt = resultsPerDisplay.get(id);
+ LOG_ALWAYS_FATAL_IF(!resultOpt);
+ targeter->endFrame(*resultOpt);
+ }
}
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
@@ -534,8 +557,16 @@
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
- auto schedule = getVsyncSchedule(id);
- LOG_ALWAYS_FATAL_IF(!schedule);
+ const auto scheduleOpt =
+ (ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) {
+ return display.powerMode == hal::PowerMode::OFF
+ ? std::nullopt
+ : std::make_optional(display.schedulePtr);
+ });
+
+ if (!scheduleOpt) return;
+ const auto& schedule = scheduleOpt->get();
+
if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
schedule->enableHardwareVsync();
} else {
@@ -724,7 +755,23 @@
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
- mPacesetterFrameTargeter.dump(dumper);
+ {
+ utils::Dumper::Section section(dumper, "Frame Targeting"sv);
+
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ for (const auto& [id, display] : mDisplays) {
+ utils::Dumper::Section
+ section(dumper,
+ id == mPacesetterDisplayId
+ ? ftl::Concat("Pacesetter Display ", id.value).c_str()
+ : ftl::Concat("Follower Display ", id.value).c_str());
+
+ display.targeterPtr->dump(dumper);
+ dumper.eol();
+ }
+ }
}
void Scheduler::dumpVsync(std::string& out) const {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 17e9cea..b913700 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -220,7 +220,7 @@
// otherwise.
bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
std::optional<nsecs_t> hwcVsyncPeriod);
- void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
+ void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>)
REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
@@ -250,7 +250,14 @@
return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
}
- const FrameTarget& pacesetterFrameTarget() { return mPacesetterFrameTargeter.target(); }
+ TimePoint expectedPresentTimeForPacesetter() const EXCLUDES(mDisplayLock) {
+ std::scoped_lock lock(mDisplayLock);
+ return pacesetterDisplayLocked()
+ .transform([](const Display& display) {
+ return display.targeterPtr->target().expectedPresentTime();
+ })
+ .value_or(TimePoint());
+ }
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
@@ -306,7 +313,8 @@
enum class TouchState { Inactive, Active };
// impl::MessageQueue overrides:
- void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override;
+ void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override
+ REQUIRES(kMainThreadContext, mDisplayLock);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -429,13 +437,24 @@
// must lock for writes but not reads. See also mPolicyLock for locking order.
mutable std::mutex mDisplayLock;
+ using FrameTargeterPtr = std::unique_ptr<FrameTargeter>;
+
struct Display {
- Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
- : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
+ Display(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ VsyncSchedulePtr schedulePtr, FeatureFlags features)
+ : displayId(displayId),
+ selectorPtr(std::move(selectorPtr)),
+ schedulePtr(std::move(schedulePtr)),
+ targeterPtr(std::make_unique<
+ FrameTargeter>(displayId,
+ features.test(Feature::kBackpressureGpuComposition))) {}
+
+ const PhysicalDisplayId displayId;
// Effectively const except in move constructor.
RefreshRateSelectorPtr selectorPtr;
VsyncSchedulePtr schedulePtr;
+ FrameTargeterPtr targeterPtr;
hal::PowerMode powerMode = hal::PowerMode::OFF;
};
@@ -449,8 +468,6 @@
ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
GUARDED_BY(kMainThreadContext);
- FrameTargeter mPacesetterFrameTargeter{mFeatures.test(Feature::kBackpressureGpuComposition)};
-
ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
[](const Display& display) { return std::ref(const_cast<Display&>(display)); });
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 85f2e64..ae74205 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -20,6 +20,7 @@
#include <atomic>
#include <memory>
+#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
@@ -75,16 +76,17 @@
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
protected:
+ explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
VsyncId mVsyncId;
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
- TracedOrdinal<bool> mFramePending{"PrevFramePending", false};
- TracedOrdinal<bool> mFrameMissed{"PrevFrameMissed", false};
- TracedOrdinal<bool> mHwcFrameMissed{"PrevHwcFrameMissed", false};
- TracedOrdinal<bool> mGpuFrameMissed{"PrevGpuFrameMissed", false};
+ TracedOrdinal<bool> mFramePending;
+ TracedOrdinal<bool> mFrameMissed;
+ TracedOrdinal<bool> mHwcFrameMissed;
+ TracedOrdinal<bool> mGpuFrameMissed;
struct FenceWithFenceTime {
sp<Fence> fence = Fence::NO_FENCE;
@@ -103,8 +105,9 @@
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
class FrameTargeter final : private FrameTarget {
public:
- explicit FrameTargeter(bool backpressureGpuComposition)
- : mBackpressureGpuComposition(backpressureGpuComposition) {}
+ FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition)
+ : FrameTarget(to_string(displayId)),
+ mBackpressureGpuComposition(backpressureGpuComposition) {}
const FrameTarget& target() const { return *this; }
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
index f795f1f..87c704e 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
@@ -16,6 +16,9 @@
#pragma once
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
#include <scheduler/interface/CompositionCoverage.h>
namespace android {
@@ -24,4 +27,6 @@
CompositionCoverageFlags compositionCoverage;
};
+using CompositeResultsPerDisplay = ui::PhysicalDisplayMap<PhysicalDisplayId, CompositeResult>;
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
index 3d0f1a9..767462d 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -19,6 +19,8 @@
#include <cstdint>
#include <ftl/flags.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
namespace android {
@@ -34,4 +36,14 @@
using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
+using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>;
+
+inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) {
+ CompositionCoverageFlags coverage;
+ for (const auto& [id, flags] : displays) {
+ coverage |= flags;
+ }
+ return coverage;
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index 2696076..6fe813a 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -16,6 +16,9 @@
#pragma once
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
#include <scheduler/interface/CompositeResult.h>
@@ -26,6 +29,8 @@
class FrameTarget;
class FrameTargeter;
+using FrameTargeters = ui::PhysicalDisplayMap<PhysicalDisplayId, scheduler::FrameTargeter*>;
+
} // namespace scheduler
struct ICompositor {
@@ -38,7 +43,8 @@
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
- virtual CompositeResult composite(scheduler::FrameTargeter&) = 0;
+ virtual CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters&) = 0;
// Samples the composited frame via RegionSamplingThread.
virtual void sample() = 0;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 7138afd..7a18654 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -21,6 +21,12 @@
namespace android::scheduler {
+FrameTarget::FrameTarget(const std::string& displayLabel)
+ : mFramePending("PrevFramePending " + displayLabel, false),
+ mFrameMissed("PrevFrameMissed " + displayLabel, false),
+ mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
+ mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
+
TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
// TODO(b/267315508): Generalize to N VSYNCs.
const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
@@ -130,10 +136,6 @@
}
void FrameTargeter::dump(utils::Dumper& dumper) const {
- using namespace std::string_view_literals;
-
- utils::Dumper::Section section(dumper, "Frame Targeting"sv);
-
// There are scripts and tests that expect this (rather than "name=value") format.
dumper.dump({}, "Total missed frame count: " + std::to_string(mFrameMissedCount));
dumper.dump({}, "HWC missed frame count: " + std::to_string(mHwcFrameMissedCount));
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 908f214..1e038d1 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -96,7 +96,7 @@
FenceToFenceTimeMap mFenceMap;
static constexpr bool kBackpressureGpuComposition = true;
- FrameTargeter mTargeter{kBackpressureGpuComposition};
+ FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition};
};
TEST_F(FrameTargeterTest, targetsFrames) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d3489ff..de0034b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2162,7 +2162,7 @@
}
}
-void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
+void SurfaceFlinger::configure() {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
setTransactionFlags(eDisplayTransactionNeeded);
@@ -2336,8 +2336,7 @@
return mustComposite;
}
-bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget)
- FTL_FAKE_GUARD(kMainThreadContext) {
+bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) {
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
@@ -2474,31 +2473,42 @@
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-CompositeResult SurfaceFlinger::composite(scheduler::FrameTargeter& pacesetterFrameTargeter)
- FTL_FAKE_GUARD(kMainThreadContext) {
- const scheduler::FrameTarget& pacesetterFrameTarget = pacesetterFrameTargeter.target();
+CompositeResultsPerDisplay SurfaceFlinger::composite(
+ PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) {
+ const scheduler::FrameTarget& pacesetterTarget =
+ frameTargeters.get(pacesetterId)->get()->target();
- const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
+ const VsyncId vsyncId = pacesetterTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
compositionengine::CompositionRefreshArgs refreshArgs;
refreshArgs.powerCallback = this;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+
+ // Add outputs for physical displays.
+ for (const auto& [id, targeter] : frameTargeters) {
+ ftl::FakeGuard guard(mStateLock);
+
+ if (const auto display = getCompositionDisplayLocked(id)) {
+ refreshArgs.outputs.push_back(display);
+ }
+ }
+
std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
displayIds.push_back(display->getId());
display->tracePowerMode();
+ // Add outputs for virtual displays.
if (display->isVirtual()) {
const Fps refreshRate = display->getAdjustedRefreshRate();
- if (refreshRate.isValid() &&
- !mScheduler->isVsyncInPhase(pacesetterFrameTarget.frameBeginTime(), refreshRate)) {
- continue;
+
+ if (!refreshRate.isValid() ||
+ mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
-
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
mPowerAdvisor->setDisplays(displayIds);
@@ -2558,15 +2568,16 @@
if (!getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- pacesetterFrameTarget.wouldPresentEarly(vsyncPeriod)) {
+ pacesetterTarget.wouldPresentEarly(vsyncPeriod)) {
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
+ // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget.
refreshArgs.earliestPresentTime =
- pacesetterFrameTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
+ pacesetterTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
}
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
- refreshArgs.expectedPresentTime = pacesetterFrameTarget.expectedPresentTime().ns();
+ refreshArgs.expectedPresentTime = pacesetterTarget.expectedPresentTime().ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
// Store the present time just before calling to the composition engine so we could notify
@@ -2592,14 +2603,14 @@
}
}
- mTimeStats->recordFrameDuration(pacesetterFrameTarget.frameBeginTime().ns(), systemTime());
+ mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
// Send a power hint after presentation is finished.
if (mPowerHintSessionEnabled) {
// Now that the current frame has been presented above, PowerAdvisor needs the present time
// of the previous frame (whose fence is signaled by now) to determine how long the HWC had
// waited on that fence to retire before presenting.
- const auto& previousPresentFence = pacesetterFrameTarget.presentFenceForPreviousFrame();
+ const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
TimePoint::now());
@@ -2610,23 +2621,27 @@
scheduleComposite(FrameHint::kNone);
}
- postComposition(pacesetterFrameTargeter, presentTime);
+ postComposition(pacesetterId, frameTargeters, presentTime);
- const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+ const bool hadGpuComposited =
+ multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu);
mCompositionCoverage.clear();
TimeStats::ClientCompositionRecord clientCompositionRecord;
+
for (const auto& [_, display] : displays) {
const auto& state = display->getCompositionDisplay()->getState();
+ CompositionCoverageFlags& flags =
+ mCompositionCoverage.try_emplace(display->getId()).first->second;
if (state.usesDeviceComposition) {
- mCompositionCoverage |= CompositionCoverage::Hwc;
+ flags |= CompositionCoverage::Hwc;
}
if (state.reusedClientComposition) {
- mCompositionCoverage |= CompositionCoverage::GpuReuse;
+ flags |= CompositionCoverage::GpuReuse;
} else if (state.usesClientComposition) {
- mCompositionCoverage |= CompositionCoverage::Gpu;
+ flags |= CompositionCoverage::Gpu;
}
clientCompositionRecord.predicted |=
@@ -2635,10 +2650,11 @@
(state.strategyPrediction == CompositionStrategyPredictionState::SUCCESS);
}
- const bool hasGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+ const auto coverage = multiDisplayUnion(mCompositionCoverage);
+ const bool hasGpuComposited = coverage.test(CompositionCoverage::Gpu);
clientCompositionRecord.hadClientComposition = hasGpuComposited;
- clientCompositionRecord.reused = mCompositionCoverage.test(CompositionCoverage::GpuReuse);
+ clientCompositionRecord.reused = coverage.test(CompositionCoverage::GpuReuse);
clientCompositionRecord.changed = hadGpuComposited != hasGpuComposited;
mTimeStats->pushCompositionStrategyState(clientCompositionRecord);
@@ -2647,13 +2663,13 @@
// TODO(b/160583065): Enable skip validation when SF caches all client composition layers.
const bool hasGpuUseOrReuse =
- mCompositionCoverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
+ coverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and should only be used for debugging.
- addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
+ addToLayerTracing(mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId);
}
if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
@@ -2667,7 +2683,16 @@
mPowerAdvisor->setCompositeEnd(TimePoint::now());
}
- return {mCompositionCoverage};
+ CompositeResultsPerDisplay resultsPerDisplay;
+
+ // Filter out virtual displays.
+ for (const auto& [id, coverage] : mCompositionCoverage) {
+ if (const auto idOpt = PhysicalDisplayId::tryCast(id)) {
+ resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage});
+ }
+ }
+
+ return resultsPerDisplay;
}
void SurfaceFlinger::updateLayerGeometry() {
@@ -2751,35 +2776,56 @@
return ui::ROTATION_0;
}
-void SurfaceFlinger::postComposition(scheduler::FrameTargeter& pacesetterFrameTargeter,
+void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters& frameTargeters,
nsecs_t presentStartTime) {
ATRACE_CALL();
ALOGV(__func__);
- const auto* defaultDisplay = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
+ ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
- std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (defaultDisplay &&
- defaultDisplay->getCompositionDisplay()->getState().usesClientComposition) {
- glCompositionDoneFenceTime =
- std::make_shared<FenceTime>(defaultDisplay->getCompositionDisplay()
- ->getRenderSurface()
- ->getClientTargetAcquireFence());
- } else {
- glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+ for (const auto& [id, targeter] : frameTargeters) {
+ auto presentFence = getHwComposer().getPresentFence(id);
+
+ if (id == pacesetterId) {
+ mTransactionCallbackInvoker.addPresentFence(presentFence);
+ }
+
+ if (auto fenceTime = targeter->setPresentFence(std::move(presentFence));
+ fenceTime->isValid()) {
+ presentFences.try_emplace(id, std::move(fenceTime));
+ }
+
+ ftl::FakeGuard guard(mStateLock);
+ if (const auto display = getCompositionDisplayLocked(id);
+ display && display->getState().usesClientComposition) {
+ gpuCompositionDoneFences
+ .try_emplace(id, display->getRenderSurface()->getClientTargetAcquireFence());
+ }
}
- auto presentFence = defaultDisplay
- ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
- : Fence::NO_FENCE;
+ const auto pacesetterDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(pacesetterId));
- auto presentFenceTime = pacesetterFrameTargeter.setPresentFence(presentFence);
+ std::shared_ptr<FenceTime> pacesetterPresentFenceTime =
+ presentFences.get(pacesetterId)
+ .transform([](const FenceTimePtr& ptr) { return ptr; })
+ .value_or(FenceTime::NO_FENCE);
+
+ std::shared_ptr<FenceTime> pacesetterGpuCompositionDoneFenceTime =
+ gpuCompositionDoneFences.get(pacesetterId)
+ .transform([](sp<Fence> fence) {
+ return std::make_shared<FenceTime>(std::move(fence));
+ })
+ .value_or(FenceTime::NO_FENCE);
+
const TimePoint presentTime = TimePoint::now();
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
- mFrameTimeline->setSfPresent(presentTime.ns(), presentFenceTime, glCompositionDoneFenceTime);
+ mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
+ pacesetterGpuCompositionDoneFenceTime);
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
@@ -2787,9 +2833,9 @@
const TimePoint compositeTime =
TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp());
const Duration presentLatency =
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
- ? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime)
- : Duration::zero();
+ getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
+ ? Duration::zero()
+ : mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime);
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
@@ -2826,8 +2872,8 @@
mLayersWithBuffersRemoved.clear();
for (const auto& layer: mLayersWithQueuedFrames) {
- layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
- compositorTiming);
+ layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime,
+ pacesetterPresentFenceTime, compositorTiming);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -2890,34 +2936,28 @@
mHdrLayerInfoChanged = false;
- mTransactionCallbackInvoker.addPresentFence(std::move(presentFence));
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
mTransactionCallbackInvoker.clearCompletedTransactions();
mTimeStats->incrementTotalFrames();
- mTimeStats->setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(pacesetterPresentFenceTime);
- {
+ for (auto&& [id, presentFence] : presentFences) {
ftl::FakeGuard guard(mStateLock);
- for (const auto& [id, physicalDisplay] : mPhysicalDisplays) {
- if (auto displayDevice = getDisplayDeviceLocked(id);
- displayDevice && displayDevice->isPoweredOn() && physicalDisplay.isInternal()) {
- auto presentFenceTimeI = defaultDisplay && defaultDisplay->getPhysicalId() == id
- ? std::move(presentFenceTime)
- : std::make_shared<FenceTime>(getHwComposer().getPresentFence(id));
- if (presentFenceTimeI->isValid()) {
- mScheduler->addPresentFence(id, std::move(presentFenceTimeI));
- }
- }
+ const bool isInternalDisplay =
+ mPhysicalDisplays.get(id).transform(&PhysicalDisplay::isInternal).value_or(false);
+
+ if (isInternalDisplay) {
+ mScheduler->addPresentFence(id, std::move(presentFence));
}
}
- const bool isDisplayConnected =
- defaultDisplay && getHwComposer().isConnected(defaultDisplay->getPhysicalId());
+ const bool hasPacesetterDisplay =
+ pacesetterDisplay && getHwComposer().isConnected(pacesetterId);
if (!hasSyncFramework) {
- if (isDisplayConnected && defaultDisplay->isPoweredOn()) {
- mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId());
+ if (hasPacesetterDisplay && pacesetterDisplay->isPoweredOn()) {
+ mScheduler->enableHardwareVsync(pacesetterId);
}
}
@@ -2925,7 +2965,7 @@
const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
- if (isDisplayConnected && !defaultDisplay->isPoweredOn()) {
+ if (hasPacesetterDisplay && !pacesetterDisplay->isPoweredOn()) {
getRenderEngine().cleanupPostRender();
return;
}
@@ -4204,7 +4244,7 @@
const auto& transaction = *flushState.transaction;
const TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
- const TimePoint expectedPresentTime = mScheduler->pacesetterFrameTarget().expectedPresentTime();
+ const TimePoint expectedPresentTime = mScheduler->expectedPresentTimeForPacesetter();
using TransactionReadiness = TransactionHandler::TransactionReadiness;
@@ -6088,8 +6128,7 @@
ftl::to_underlying(windowInfosDebug.maxSendDelayVsyncId));
StringAppendF(&result, " max send delay (ns): %" PRId64 " ns\n",
windowInfosDebug.maxSendDelayDuration);
- StringAppendF(&result, " unsent messages: %" PRIu32 "\n",
- windowInfosDebug.pendingMessageCount);
+ StringAppendF(&result, " unsent messages: %zu\n", windowInfosDebug.pendingMessageCount);
result.append("\n");
}
@@ -7352,7 +7391,10 @@
renderArea->getHintForSeamlessTransition());
sdrWhitePointNits = state.sdrWhitePointNits;
displayBrightnessNits = state.displayBrightnessNits;
- if (sdrWhitePointNits > 1.0f) {
+ // Only clamp the display brightness if this is not a seamless transition. Otherwise
+ // for seamless transitions it's important to match the current display state as the
+ // buffer will be shown under these same conditions, and we want to avoid any flickers
+ if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
// Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming
// the SDR portion. 2.0 chosen by experimentation
constexpr float kMaxScreenshotHeadroom = 2.0f;
@@ -7926,9 +7968,9 @@
forceApplyPolicy);
}
-status_t SurfaceFlinger::addWindowInfosListener(
- const sp<IWindowInfosListener>& windowInfosListener) {
- mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+status_t SurfaceFlinger::addWindowInfosListener(const sp<IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outInfo) {
+ mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener, outInfo);
setTransactionFlags(eInputInfoUpdateNeeded);
return NO_ERROR;
}
@@ -9011,7 +9053,8 @@
}
binder::Status SurfaceComposerAIDL::addWindowInfosListener(
- const sp<gui::IWindowInfosListener>& windowInfosListener) {
+ const sp<gui::IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outInfo) {
status_t status;
const int pid = IPCThreadState::self()->getCallingPid();
const int uid = IPCThreadState::self()->getCallingUid();
@@ -9019,7 +9062,7 @@
// WindowInfosListeners
if (uid == AID_SYSTEM || uid == AID_GRAPHICS ||
checkPermission(sAccessSurfaceFlinger, pid, uid)) {
- status = mFlinger->addWindowInfosListener(windowInfosListener);
+ status = mFlinger->addWindowInfosListener(windowInfosListener, outInfo);
} else {
status = PERMISSION_DENIED;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e27d21f..f1759a5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -613,7 +613,8 @@
status_t getMaxAcquiredBufferCount(int* buffers) const;
- status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+ status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outResult);
status_t removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) const;
@@ -632,9 +633,12 @@
void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
// ICompositor overrides:
- void configure() override;
- bool commit(const scheduler::FrameTarget&) override;
- CompositeResult composite(scheduler::FrameTargeter&) override;
+ void configure() override REQUIRES(kMainThreadContext);
+ bool commit(const scheduler::FrameTarget&) override REQUIRES(kMainThreadContext);
+ CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters&) override
+ REQUIRES(kMainThreadContext);
+
void sample() override;
// ISchedulerCallback overrides:
@@ -883,6 +887,14 @@
return findDisplay([id](const auto& display) { return display.getId() == id; });
}
+ std::shared_ptr<compositionengine::Display> getCompositionDisplayLocked(DisplayId id) const
+ REQUIRES(mStateLock) {
+ if (const auto display = getDisplayDeviceLocked(id)) {
+ return display->getCompositionDisplay();
+ }
+ return nullptr;
+ }
+
// Returns the primary display or (for foldables) the active display, assuming that the inner
// and outer displays have mutually exclusive power states.
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
@@ -956,8 +968,8 @@
/*
* Compositing
*/
- void postComposition(scheduler::FrameTargeter&, nsecs_t presentStartTime)
- REQUIRES(kMainThreadContext);
+ void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
+ nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
/*
* Display management
@@ -1289,7 +1301,7 @@
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
- CompositionCoverageFlags mCompositionCoverage;
+ CompositionCoveragePerDisplay mCompositionCoverage;
// mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
// any mutex.
@@ -1526,8 +1538,8 @@
binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
binder::Status getGpuContextPriority(int32_t* outPriority) override;
binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
- binder::Status addWindowInfosListener(
- const sp<gui::IWindowInfosListener>& windowInfosListener) override;
+ binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
+ gui::WindowInfosListenerInfo* outInfo) override;
binder::Status removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) override;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 20699ef..7062a4e 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-#include <ftl/small_vector.h>
+#include <android/gui/BnWindowInfosPublisher.h>
+#include <android/gui/IWindowInfosPublisher.h>
+#include <android/gui/WindowInfosListenerInfo.h>
#include <gui/ISurfaceComposer.h>
#include <gui/TraceUtils.h>
#include <gui/WindowInfosUpdate.h>
@@ -23,162 +25,130 @@
#include "BackgroundExecutor.h"
#include "WindowInfosListenerInvoker.h"
+#undef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
namespace android {
using gui::DisplayInfo;
using gui::IWindowInfosListener;
using gui::WindowInfo;
-using WindowInfosListenerVector = ftl::SmallVector<const sp<gui::IWindowInfosListener>, 3>;
+void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener,
+ gui::WindowInfosListenerInfo* outInfo) {
+ int64_t listenerId = mNextListenerId++;
+ outInfo->listenerId = listenerId;
+ outInfo->windowInfosPublisher = sp<gui::IWindowInfosPublisher>::fromExisting(this);
-struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
- IBinder::DeathRecipient {
- WindowInfosReportedListenerInvoker(WindowInfosListenerVector windowInfosListeners,
- WindowInfosReportedListenerSet windowInfosReportedListeners)
- : mCallbacksPending(windowInfosListeners.size()),
- mWindowInfosListeners(std::move(windowInfosListeners)),
- mWindowInfosReportedListeners(std::move(windowInfosReportedListeners)) {}
-
- binder::Status onWindowInfosReported() override {
- if (--mCallbacksPending == 0) {
- for (const auto& listener : mWindowInfosReportedListeners) {
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[this, listener = std::move(listener), listenerId]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::addWindowInfosListener");
sp<IBinder> asBinder = IInterface::asBinder(listener);
- if (asBinder->isBinderAlive()) {
- listener->onWindowInfosReported();
- }
- }
-
- auto wpThis = wp<WindowInfosReportedListenerInvoker>::fromExisting(this);
- for (const auto& listener : mWindowInfosListeners) {
- sp<IBinder> binder = IInterface::asBinder(listener);
- binder->unlinkToDeath(wpThis);
- }
- }
- return binder::Status::ok();
- }
-
- void binderDied(const wp<IBinder>&) { onWindowInfosReported(); }
-
-private:
- std::atomic<size_t> mCallbacksPending;
- static constexpr size_t kStaticCapacity = 3;
- const WindowInfosListenerVector mWindowInfosListeners;
- WindowInfosReportedListenerSet mWindowInfosReportedListeners;
-};
-
-void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
- asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
-
- std::scoped_lock lock(mListenersMutex);
- mWindowInfosListeners.try_emplace(asBinder, std::move(listener));
+ asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
+ mWindowInfosListeners.try_emplace(asBinder,
+ std::make_pair(listenerId, std::move(listener)));
+ }});
}
void WindowInfosListenerInvoker::removeWindowInfosListener(
const sp<IWindowInfosListener>& listener) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
-
- std::scoped_lock lock(mListenersMutex);
- asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
- mWindowInfosListeners.erase(asBinder);
+ BackgroundExecutor::getInstance().sendCallbacks({[this, listener]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener");
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
+ mWindowInfosListeners.erase(asBinder);
+ }});
}
void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
- std::scoped_lock lock(mListenersMutex);
- mWindowInfosListeners.erase(who);
+ BackgroundExecutor::getInstance().sendCallbacks({[this, who]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::binderDied");
+ auto it = mWindowInfosListeners.find(who);
+ int64_t listenerId = it->second.first;
+ mWindowInfosListeners.erase(who);
+
+ std::vector<int64_t> vsyncIds;
+ for (auto& [vsyncId, state] : mUnackedState) {
+ if (std::find(state.unackedListenerIds.begin(), state.unackedListenerIds.end(),
+ listenerId) != state.unackedListenerIds.end()) {
+ vsyncIds.push_back(vsyncId);
+ }
+ }
+
+ for (int64_t vsyncId : vsyncIds) {
+ ackWindowInfosReceived(vsyncId, listenerId);
+ }
+ }});
}
void WindowInfosListenerInvoker::windowInfosChanged(
gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
bool forceImmediateCall) {
- WindowInfosListenerVector listeners;
- {
- std::scoped_lock lock{mMessagesMutex};
+ if (!mDelayInfo) {
+ mDelayInfo = DelayInfo{
+ .vsyncId = update.vsyncId,
+ .frameTime = update.timestamp,
+ };
+ }
- if (!mDelayInfo) {
- mDelayInfo = DelayInfo{
- .vsyncId = update.vsyncId,
- .frameTime = update.timestamp,
- };
- }
+ // If there are unacked messages and this isn't a forced call, then return immediately.
+ // If a forced window infos change doesn't happen first, the update will be sent after
+ // the WindowInfosReportedListeners are called. If a forced window infos change happens or
+ // if there are subsequent delayed messages before this update is sent, then this message
+ // will be dropped and the listeners will only be called with the latest info. This is done
+ // to reduce the amount of binder memory used.
+ if (!mUnackedState.empty() && !forceImmediateCall) {
+ mDelayedUpdate = std::move(update);
+ mReportedListeners.merge(reportedListeners);
+ return;
+ }
- // If there are unacked messages and this isn't a forced call, then return immediately.
- // If a forced window infos change doesn't happen first, the update will be sent after
- // the WindowInfosReportedListeners are called. If a forced window infos change happens or
- // if there are subsequent delayed messages before this update is sent, then this message
- // will be dropped and the listeners will only be called with the latest info. This is done
- // to reduce the amount of binder memory used.
- if (mActiveMessageCount > 0 && !forceImmediateCall) {
- mDelayedUpdate = std::move(update);
- mReportedListeners.merge(reportedListeners);
- return;
- }
+ if (mDelayedUpdate) {
+ mDelayedUpdate.reset();
+ }
- if (mDelayedUpdate) {
- mDelayedUpdate.reset();
- }
-
- {
- std::scoped_lock lock{mListenersMutex};
- for (const auto& [_, listener] : mWindowInfosListeners) {
- listeners.push_back(listener);
- }
- }
- if (CC_UNLIKELY(listeners.empty())) {
- mReportedListeners.merge(reportedListeners);
- mDelayInfo.reset();
- return;
- }
-
- reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
- reportedListeners.merge(mReportedListeners);
- mReportedListeners.clear();
-
- mActiveMessageCount++;
- updateMaxSendDelay();
+ if (CC_UNLIKELY(mWindowInfosListeners.empty())) {
+ mReportedListeners.merge(reportedListeners);
mDelayInfo.reset();
+ return;
}
- auto reportedInvoker =
- sp<WindowInfosReportedListenerInvoker>::make(listeners, std::move(reportedListeners));
+ reportedListeners.merge(mReportedListeners);
+ mReportedListeners.clear();
- for (const auto& listener : listeners) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
+ // Update mUnackedState to include the message we're about to send
+ auto [it, _] = mUnackedState.try_emplace(update.vsyncId,
+ UnackedState{.reportedListeners =
+ std::move(reportedListeners)});
+ auto& unackedState = it->second;
+ for (auto& pair : mWindowInfosListeners) {
+ int64_t listenerId = pair.second.first;
+ unackedState.unackedListenerIds.push_back(listenerId);
+ }
- // linkToDeath is used here to ensure that the windowInfosReportedListeners
- // are called even if one of the windowInfosListeners dies before
- // calling onWindowInfosReported.
- asBinder->linkToDeath(reportedInvoker);
+ mDelayInfo.reset();
+ updateMaxSendDelay();
- auto status = listener->onWindowInfosChanged(update, reportedInvoker);
+ // Call the listeners
+ for (auto& pair : mWindowInfosListeners) {
+ auto& [listenerId, listener] = pair.second;
+ auto status = listener->onWindowInfosChanged(update);
if (!status.isOk()) {
- reportedInvoker->onWindowInfosReported();
+ ackWindowInfosReceived(update.vsyncId, listenerId);
}
}
}
-binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
- BackgroundExecutor::getInstance().sendCallbacks({[this]() {
- gui::WindowInfosUpdate update;
- {
- std::scoped_lock lock{mMessagesMutex};
- mActiveMessageCount--;
- if (!mDelayedUpdate || mActiveMessageCount > 0) {
- return;
- }
- update = std::move(*mDelayedUpdate);
- mDelayedUpdate.reset();
- }
- windowInfosChanged(std::move(update), {}, false);
- }});
- return binder::Status::ok();
-}
-
WindowInfosListenerInvoker::DebugInfo WindowInfosListenerInvoker::getDebugInfo() {
- std::scoped_lock lock{mMessagesMutex};
- updateMaxSendDelay();
- mDebugInfo.pendingMessageCount = mActiveMessageCount;
- return mDebugInfo;
+ DebugInfo result;
+ BackgroundExecutor::getInstance().sendCallbacks({[&, this]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::getDebugInfo");
+ updateMaxSendDelay();
+ result = mDebugInfo;
+ result.pendingMessageCount = mUnackedState.size();
+ }});
+ BackgroundExecutor::getInstance().flushQueue();
+ return result;
}
void WindowInfosListenerInvoker::updateMaxSendDelay() {
@@ -192,4 +162,41 @@
}
}
+binder::Status WindowInfosListenerInvoker::ackWindowInfosReceived(int64_t vsyncId,
+ int64_t listenerId) {
+ BackgroundExecutor::getInstance().sendCallbacks({[this, vsyncId, listenerId]() {
+ ATRACE_NAME("WindowInfosListenerInvoker::ackWindowInfosReceived");
+ auto it = mUnackedState.find(vsyncId);
+ if (it == mUnackedState.end()) {
+ return;
+ }
+
+ auto& state = it->second;
+ state.unackedListenerIds.unstable_erase(std::find(state.unackedListenerIds.begin(),
+ state.unackedListenerIds.end(),
+ listenerId));
+ if (!state.unackedListenerIds.empty()) {
+ return;
+ }
+
+ WindowInfosReportedListenerSet reportedListeners{std::move(state.reportedListeners)};
+ mUnackedState.erase(vsyncId);
+
+ for (const auto& reportedListener : reportedListeners) {
+ sp<IBinder> asBinder = IInterface::asBinder(reportedListener);
+ if (asBinder->isBinderAlive()) {
+ reportedListener->onWindowInfosReported();
+ }
+ }
+
+ if (!mDelayedUpdate || !mUnackedState.empty()) {
+ return;
+ }
+ gui::WindowInfosUpdate update{std::move(*mDelayedUpdate)};
+ mDelayedUpdate.reset();
+ windowInfosChanged(std::move(update), {}, false);
+ }});
+ return binder::Status::ok();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index bc465a3..f36b0ed 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -19,11 +19,12 @@
#include <optional>
#include <unordered_set>
-#include <android/gui/BnWindowInfosReportedListener.h>
+#include <android/gui/BnWindowInfosPublisher.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <binder/IBinder.h>
#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
#include <gui/SpHash.h>
#include <utils/Mutex.h>
@@ -35,22 +36,22 @@
std::unordered_set<sp<gui::IWindowInfosReportedListener>,
gui::SpHash<gui::IWindowInfosReportedListener>>;
-class WindowInfosListenerInvoker : public gui::BnWindowInfosReportedListener,
+class WindowInfosListenerInvoker : public gui::BnWindowInfosPublisher,
public IBinder::DeathRecipient {
public:
- void addWindowInfosListener(sp<gui::IWindowInfosListener>);
+ void addWindowInfosListener(sp<gui::IWindowInfosListener>, gui::WindowInfosListenerInfo*);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
void windowInfosChanged(gui::WindowInfosUpdate update,
WindowInfosReportedListenerSet windowInfosReportedListeners,
bool forceImmediateCall);
- binder::Status onWindowInfosReported() override;
+ binder::Status ackWindowInfosReceived(int64_t, int64_t) override;
struct DebugInfo {
VsyncId maxSendDelayVsyncId;
nsecs_t maxSendDelayDuration;
- uint32_t pendingMessageCount;
+ size_t pendingMessageCount;
};
DebugInfo getDebugInfo();
@@ -58,24 +59,28 @@
void binderDied(const wp<IBinder>& who) override;
private:
- std::mutex mListenersMutex;
-
static constexpr size_t kStaticCapacity = 3;
- ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
- mWindowInfosListeners GUARDED_BY(mListenersMutex);
+ std::atomic<int64_t> mNextListenerId{0};
+ ftl::SmallMap<wp<IBinder>, const std::pair<int64_t, sp<gui::IWindowInfosListener>>,
+ kStaticCapacity>
+ mWindowInfosListeners;
- std::mutex mMessagesMutex;
- uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
- std::optional<gui::WindowInfosUpdate> mDelayedUpdate GUARDED_BY(mMessagesMutex);
+ std::optional<gui::WindowInfosUpdate> mDelayedUpdate;
WindowInfosReportedListenerSet mReportedListeners;
- DebugInfo mDebugInfo GUARDED_BY(mMessagesMutex);
+ struct UnackedState {
+ ftl::SmallVector<int64_t, kStaticCapacity> unackedListenerIds;
+ WindowInfosReportedListenerSet reportedListeners;
+ };
+ ftl::SmallMap<int64_t /* vsyncId */, UnackedState, 5> mUnackedState;
+
+ DebugInfo mDebugInfo;
struct DelayInfo {
int64_t vsyncId;
nsecs_t frameTime;
};
- std::optional<DelayInfo> mDelayInfo GUARDED_BY(mMessagesMutex);
- void updateMaxSendDelay() REQUIRES(mMessagesMutex);
+ std::optional<DelayInfo> mDelayInfo;
+ void updateMaxSendDelay();
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 0c9a16b..36095b4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -287,7 +287,10 @@
// ICompositor overrides:
void configure() override {}
bool commit(const scheduler::FrameTarget&) override { return false; }
- CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
// MessageQueue overrides:
@@ -474,25 +477,25 @@
&outWideColorGamutPixelFormat);
}
- void overrideHdrTypes(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+ void overrideHdrTypes(const sp<IBinder>& display, FuzzedDataProvider* fdp) {
std::vector<ui::Hdr> hdrTypes;
hdrTypes.push_back(fdp->PickValueInArray(kHdrTypes));
mFlinger->overrideHdrTypes(display, hdrTypes);
}
- void getDisplayedContentSample(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+ void getDisplayedContentSample(const sp<IBinder>& display, FuzzedDataProvider* fdp) {
DisplayedFrameStats outDisplayedFrameStats;
mFlinger->getDisplayedContentSample(display, fdp->ConsumeIntegral<uint64_t>(),
fdp->ConsumeIntegral<uint64_t>(),
&outDisplayedFrameStats);
}
- void getDisplayStats(sp<IBinder> &display) {
+ void getDisplayStats(const sp<IBinder>& display) {
android::DisplayStatInfo stats;
mFlinger->getDisplayStats(display, &stats);
}
- void getDisplayState(sp<IBinder> &display) {
+ void getDisplayState(const sp<IBinder>& display) {
ui::DisplayState displayState;
mFlinger->getDisplayState(display, &displayState);
}
@@ -506,12 +509,12 @@
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
- void getDisplayNativePrimaries(sp<IBinder> &display) {
+ void getDisplayNativePrimaries(const sp<IBinder>& display) {
android::ui::DisplayPrimaries displayPrimaries;
mFlinger->getDisplayNativePrimaries(display, displayPrimaries);
}
- void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
+ void getDesiredDisplayModeSpecs(const sp<IBinder>& display) {
gui::DisplayModeSpecs _;
mFlinger->getDesiredDisplayModeSpecs(display, &_);
}
@@ -523,7 +526,7 @@
return ids.front();
}
- std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
+ std::pair<sp<IBinder>, PhysicalDisplayId> fuzzBoot(FuzzedDataProvider* fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -550,13 +553,13 @@
mFlinger->bootFinished();
- return {display, physicalDisplayId.value};
+ return {display, physicalDisplayId};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- auto [display, displayId] = fuzzBoot(&mFdp);
+ const auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -564,8 +567,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(displayId);
- getDynamicDisplayInfo(displayId);
+ getStaticDisplayInfo(displayId.value);
+ getDynamicDisplayInfo(displayId.value);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
@@ -605,8 +608,9 @@
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
- mFlinger->postComposition(frameTargeter, mFdp.ConsumeIntegral<nsecs_t>());
+ scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
+ mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter),
+ mFdp.ConsumeIntegral<nsecs_t>());
}
mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index b1fd06f..4d1a5ff 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -50,7 +50,7 @@
constexpr uint16_t kRandomStringLength = 256;
constexpr std::chrono::duration kSyncPeriod(16ms);
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr PhysicalDisplayId kDisplayId = PhysicalDisplayId::fromPort(42u);
template <typename T>
void dump(T* component, FuzzedDataProvider* fdp) {
@@ -177,9 +177,8 @@
uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
- scheduler::VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
- mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
- minimumSamplesForPrediction,
+ scheduler::VSyncPredictor tracker{kDisplayId, mFdp.ConsumeIntegral<uint16_t>() /*period*/,
+ historySize, minimumSamplesForPrediction,
mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
tracker.setPeriod(period);
@@ -251,7 +250,7 @@
void SchedulerFuzzer::fuzzVSyncReactor() {
std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
- scheduler::VSyncReactor reactor(DEFAULT_DISPLAY_ID,
+ scheduler::VSyncReactor reactor(kDisplayId,
std::make_unique<ClockWrapper>(
std::make_shared<FuzzImplClock>()),
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
@@ -408,7 +407,7 @@
}
void SchedulerFuzzer::fuzzFrameTargeter() {
- scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
+ scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
const struct VsyncSource final : scheduler::IVsyncSource {
explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 359e2ab..1dcf222 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -36,7 +36,10 @@
struct NoOpCompositor final : ICompositor {
void configure() override {}
bool commit(const scheduler::FrameTarget&) override { return false; }
- CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
} gNoOpCompositor;
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index d63e187..aaf55fb 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -222,6 +222,7 @@
makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10);
+ static inline const DisplayModes kModes_60_90_120 = makeModes(kMode60, kMode90, kMode120);
// This is a typical TV configuration.
static inline const DisplayModes kModes_24_25_30_50_60_Frac =
@@ -1413,7 +1414,9 @@
ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
+ const auto bestFps = selector.getBestFrameRateMode(layers)->getFps();
+ EXPECT_EQ(expected, bestFps)
+ << "expected " << expected << " for " << desired << " but got " << bestFps;
}
}
@@ -1422,7 +1425,7 @@
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
- // Test that 23.976 will choose 24 if 23.976 is not supported
+ // Test that 23.976 will prefer 60 over 59.94 and 30
{
auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
kMode60Frac),
@@ -1431,7 +1434,7 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 23.976_Hz;
lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 24 will choose 23.976 if 24 is not supported
@@ -1456,13 +1459,13 @@
EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
}
- // Test that 29.97 will choose 30 if 59.94 is not supported
+ // Test that 29.97 will choose 60 if 59.94 is not supported
{
auto selector = createSelector(makeModes(kMode30, kMode60), kModeId60);
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId30, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 59.94 will choose 60 if 59.94 is not supported
@@ -2516,6 +2519,71 @@
EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
}
+TEST_P(RefreshRateSelectorTest, test23976Chooses120) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "23.976 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 23.976_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test23976Chooses60IfThresholdIs120) {
+ auto selector =
+ createSelector(kModes_60_90_120, kModeId120, {.frameRateMultipleThreshold = 120});
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "23.976 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 23.976_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test25Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "25 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 25.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test2997Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "29.97 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 29.97_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test50Chooses120) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "50 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 50.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test50Chooses60IfThresholdIs120) {
+ auto selector =
+ createSelector(kModes_60_90_120, kModeId120, {.frameRateMultipleThreshold = 120});
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "50 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 50.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test5994Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "59.94 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 59.94_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
@@ -3042,5 +3110,20 @@
EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
}
+TEST_P(RefreshRateSelectorTest, frameRateIsLowerThanMinSupported) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ constexpr Fps kMin = RefreshRateSelector::kMinSupportedFrameRate;
+ constexpr FpsRanges kLowerThanMin = {{60_Hz, 90_Hz}, {kMin / 2, kMin / 2}};
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {DisplayModeId(kModeId60), kLowerThanMin, kLowerThanMin}));
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index aac11c0..a978984 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -174,7 +174,10 @@
// ICompositor overrides:
void configure() override {}
bool commit(const scheduler::FrameTarget&) override { return false; }
- CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ return {};
+ }
void sample() override {}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 833984f..da482d5 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -353,7 +353,10 @@
* Forwarding for functions being tested
*/
- void configure() { mFlinger->configure(); }
+ void configure() {
+ ftl::FakeGuard guard(kMainThreadContext);
+ mFlinger->configure();
+ }
void configureAndCommit() {
configure();
@@ -362,8 +365,14 @@
void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
bool composite = false) {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ const auto displayIdOpt = mScheduler->pacesetterDisplayId();
+ LOG_ALWAYS_FATAL_IF(!displayIdOpt);
+ const auto displayId = *displayIdOpt;
+
constexpr bool kBackpressureGpuComposition = true;
- scheduler::FrameTargeter frameTargeter(kBackpressureGpuComposition);
+ scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition);
frameTargeter.beginFrame({.frameBeginTime = frameTime,
.vsyncId = vsyncId,
@@ -374,7 +383,7 @@
mFlinger->commit(frameTargeter.target());
if (composite) {
- mFlinger->composite(frameTargeter);
+ mFlinger->composite(displayId, ftl::init::map(displayId, &frameTargeter));
}
}
diff --git a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
index af4971b..c7b845e 100644
--- a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
@@ -15,35 +15,23 @@
WindowInfosListenerInvokerTest() : mInvoker(sp<WindowInfosListenerInvoker>::make()) {}
~WindowInfosListenerInvokerTest() {
- std::mutex mutex;
- std::condition_variable cv;
- bool flushComplete = false;
// Flush the BackgroundExecutor thread to ensure any scheduled tasks are complete.
// Otherwise, references those tasks hold may go out of scope before they are done
// executing.
- BackgroundExecutor::getInstance().sendCallbacks({[&]() {
- std::scoped_lock lock{mutex};
- flushComplete = true;
- cv.notify_one();
- }});
- std::unique_lock<std::mutex> lock{mutex};
- cv.wait(lock, [&]() { return flushComplete; });
+ BackgroundExecutor::getInstance().flushQueue();
}
sp<WindowInfosListenerInvoker> mInvoker;
};
-using WindowInfosUpdateConsumer = std::function<void(const gui::WindowInfosUpdate&,
- const sp<gui::IWindowInfosReportedListener>&)>;
+using WindowInfosUpdateConsumer = std::function<void(const gui::WindowInfosUpdate&)>;
class Listener : public gui::BnWindowInfosListener {
public:
Listener(WindowInfosUpdateConsumer consumer) : mConsumer(std::move(consumer)) {}
- binder::Status onWindowInfosChanged(
- const gui::WindowInfosUpdate& update,
- const sp<gui::IWindowInfosReportedListener>& reportedListener) override {
- mConsumer(update, reportedListener);
+ binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
+ mConsumer(update);
return binder::Status::ok();
}
@@ -58,15 +46,17 @@
int callCount = 0;
- mInvoker->addWindowInfosListener(
- sp<Listener>::make([&](const gui::WindowInfosUpdate&,
- const sp<gui::IWindowInfosReportedListener>& reportedListener) {
- std::scoped_lock lock{mutex};
- callCount++;
- cv.notify_one();
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate& update) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
- reportedListener->onWindowInfosReported();
- }));
+ listenerInfo.windowInfosPublisher
+ ->ackWindowInfosReceived(update.vsyncId,
+ listenerInfo.listenerId);
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks(
{[this]() { mInvoker->windowInfosChanged({}, {}, false); }});
@@ -81,21 +71,27 @@
std::mutex mutex;
std::condition_variable cv;
- int callCount = 0;
- const int expectedCallCount = 3;
+ size_t callCount = 0;
+ const size_t expectedCallCount = 3;
+ std::vector<gui::WindowInfosListenerInfo> listenerInfos{expectedCallCount,
+ gui::WindowInfosListenerInfo{}};
- for (int i = 0; i < expectedCallCount; i++) {
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&,
- const sp<gui::IWindowInfosReportedListener>& reportedListener) {
- std::scoped_lock lock{mutex};
- callCount++;
- if (callCount == expectedCallCount) {
- cv.notify_one();
- }
+ for (size_t i = 0; i < expectedCallCount; i++) {
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&, &listenerInfo = listenerInfos[i]](
+ const gui::WindowInfosUpdate&
+ update) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ if (callCount == expectedCallCount) {
+ cv.notify_one();
+ }
- reportedListener->onWindowInfosReported();
- }));
+ listenerInfo.windowInfosPublisher
+ ->ackWindowInfosReceived(update.vsyncId,
+ listenerInfo
+ .listenerId);
+ }),
+ &listenerInfos[i]);
}
BackgroundExecutor::getInstance().sendCallbacks(
@@ -114,17 +110,20 @@
int callCount = 0;
- // Simulate a slow ack by not calling the WindowInfosReportedListener.
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- callCount++;
- cv.notify_one();
- }));
+ // Simulate a slow ack by not calling IWindowInfosPublisher.ackWindowInfosReceived
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
- mInvoker->windowInfosChanged({}, {}, false);
- mInvoker->windowInfosChanged({}, {}, false);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 0, 0}, {},
+ false);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 1, 0}, {},
+ false);
}});
{
@@ -134,7 +133,7 @@
EXPECT_EQ(callCount, 1);
// Ack the first message.
- mInvoker->onWindowInfosReported();
+ listenerInfo.windowInfosPublisher->ackWindowInfosReceived(0, listenerInfo.listenerId);
{
std::unique_lock lock{mutex};
@@ -152,19 +151,21 @@
int callCount = 0;
const int expectedCallCount = 2;
- // Simulate a slow ack by not calling the WindowInfosReportedListener.
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- callCount++;
- if (callCount == expectedCallCount) {
- cv.notify_one();
- }
- }));
+ // Simulate a slow ack by not calling IWindowInfosPublisher.ackWindowInfosReceived
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ if (callCount == expectedCallCount) {
+ cv.notify_one();
+ }
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
- mInvoker->windowInfosChanged({}, {}, false);
- mInvoker->windowInfosChanged({}, {}, true);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 0, 0}, {},
+ false);
+ mInvoker->windowInfosChanged(gui::WindowInfosUpdate{{}, {}, /* vsyncId= */ 1, 0}, {}, true);
}});
{
@@ -182,14 +183,14 @@
int64_t lastUpdateId = -1;
- // Simulate a slow ack by not calling the WindowInfosReportedListener.
- mInvoker->addWindowInfosListener(
- sp<Listener>::make([&](const gui::WindowInfosUpdate& update,
- const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- lastUpdateId = update.vsyncId;
- cv.notify_one();
- }));
+ // Simulate a slow ack by not calling IWindowInfosPublisher.ackWindowInfosReceived
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate& update) {
+ std::scoped_lock lock{mutex};
+ lastUpdateId = update.vsyncId;
+ cv.notify_one();
+ }),
+ &listenerInfo);
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 1, 0}, {}, false);
@@ -204,7 +205,7 @@
EXPECT_EQ(lastUpdateId, 1);
// Ack the first message. The third update should be sent.
- mInvoker->onWindowInfosReported();
+ listenerInfo.windowInfosPublisher->ackWindowInfosReceived(1, listenerInfo.listenerId);
{
std::unique_lock lock{mutex};
@@ -225,14 +226,17 @@
// delayed.
BackgroundExecutor::getInstance().sendCallbacks({[&]() {
mInvoker->windowInfosChanged({}, {}, false);
- mInvoker->addWindowInfosListener(sp<Listener>::make(
- [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
- std::scoped_lock lock{mutex};
- callCount++;
- cv.notify_one();
- }));
- mInvoker->windowInfosChanged({}, {}, false);
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate&) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ }),
+ &listenerInfo);
}});
+ BackgroundExecutor::getInstance().flushQueue();
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
{
std::unique_lock lock{mutex};